browser-console-mcp 1.0.1 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/copy-html2canvas.js +26 -2
- package/dist/browser/index.js +1 -1
- package/dist/client/browser-console-mcp.js +1 -1
- package/dist/client/index.js +13 -13
- package/dist/server/index.js +9 -9
- package/dist/server/static-server.js +4 -4
- package/package.json +10 -2
- package/readme.md +110 -196
- package/dist/browser/index.js.map +0 -1
- package/dist/client/browser-console-mcp.js.map +0 -1
- package/rollup.browser.config.js +0 -23
- package/rollup.config.js +0 -22
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"browser-console-mcp.js","sources":["../../src/client/index.ts"],"sourcesContent":["/**\n * Browser Console MCP Client\n *\n * This client runs in the browser console and communicates with the MCP server\n */\n\ninterface MCPMessage {\n\ttype: string;\n\tpayload?: Record<string, unknown>;\n\trequestId?: string;\n\tcode?: string;\n\tmessage?: string;\n\tselector?: string;\n\ttext?: string;\n}\n\ninterface MCPConsole {\n\texec: (command: string) => string;\n\tdisconnect: () => string;\n\treconnect: () => string;\n\thelp: () => string;\n}\n\n// html2canvas options type\ninterface Html2CanvasOptions {\n\tallowTaint?: boolean;\n\tuseCORS?: boolean;\n\tlogging?: boolean;\n\tscale?: number;\n\tbackgroundColor?: string | null;\n\tremoveContainer?: boolean;\n\tscrollX?: number;\n\tscrollY?: number;\n\twindowWidth?: number;\n\twindowHeight?: number;\n\tx?: number;\n\ty?: number;\n\twidth?: number;\n\theight?: number;\n\t[key: string]: boolean | number | string | undefined | null;\n}\n\n// Extend global Window interface\ndeclare global {\n\tinterface Window {\n\t\tmcp: MCPConsole;\n\t}\n\n\tinterface WindowWithHtml2Canvas extends Window {\n\t\thtml2canvas?: (\n\t\t\telement: HTMLElement,\n\t\t\toptions?: Html2CanvasOptions,\n\t\t) => Promise<HTMLCanvasElement>;\n\t}\n}\n\nclass BrowserConsoleMCP {\n\tprivate ws: WebSocket | null = null;\n\tprivate serverUrl: string;\n\tprivate connected = false;\n\tprivate commandHistory: string[] = [];\n\tprivate historyIndex = -1;\n\n\tconstructor(serverUrl = \"ws://localhost:7898/browser\") {\n\t\tthis.serverUrl = serverUrl;\n\t}\n\n\t/**\n\t * Connect to MCP server\n\t */\n\tconnect(): void {\n\t\ttry {\n\t\t\t// First load html2canvas\n\t\t\tthis.loadHtml2Canvas()\n\t\t\t\t.then(() => {\n\t\t\t\t\tthis.ws = new WebSocket(this.serverUrl);\n\n\t\t\t\t\tthis.ws.onopen = () => {\n\t\t\t\t\t\tthis.connected = true;\n\t\t\t\t\t\tconsole.info(\"%c[MCP Client] Connected to server\", \"color: green\");\n\t\t\t\t\t\tthis.registerConsoleCommands();\n\t\t\t\t\t};\n\n\t\t\t\t\tthis.ws.onmessage = (event) => {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst message: MCPMessage = JSON.parse(event.data);\n\t\t\t\t\t\t\tthis.handleServerMessage(message);\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tconsole.error(\"[MCP Client] Message parsing error:\", error);\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\n\t\t\t\t\tthis.ws.onclose = () => {\n\t\t\t\t\t\tthis.connected = false;\n\t\t\t\t\t\tconsole.info(\n\t\t\t\t\t\t\t\"%c[MCP Client] Disconnected from server\",\n\t\t\t\t\t\t\t\"color: orange\",\n\t\t\t\t\t\t);\n\t\t\t\t\t};\n\n\t\t\t\t\tthis.ws.onerror = (error) => {\n\t\t\t\t\t\tconsole.error(\"[MCP Client] WebSocket error:\", error);\n\t\t\t\t\t};\n\t\t\t\t})\n\t\t\t\t.catch((error) => {\n\t\t\t\t\tconsole.error(\"[MCP Client] Failed to load html2canvas:\", error);\n\t\t\t\t});\n\t\t} catch (error) {\n\t\t\tconsole.error(\"[MCP Client] Connection error:\", error);\n\t\t}\n\t}\n\n\t/**\n\t * Load html2canvas library\n\t */\n\tprivate loadHtml2Canvas(): Promise<boolean> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tif (\n\t\t\t\ttypeof (window as WindowWithHtml2Canvas).html2canvas !== \"undefined\"\n\t\t\t) {\n\t\t\t\tconsole.info(\"[MCP Client] html2canvas already loaded\");\n\t\t\t\tresolve(true);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconsole.info(\"[MCP Client] Loading html2canvas...\");\n\n\t\t\t// Prioritize loading html2canvas from CDN\n\t\t\tconst script = document.createElement(\"script\");\n\t\t\tscript.src = \"https://html2canvas.hertzen.com/dist/html2canvas.min.js\";\n\t\t\tscript.onload = () => {\n\t\t\t\tconsole.info(\"[MCP Client] html2canvas loaded from CDN\");\n\t\t\t\tresolve(true);\n\t\t\t};\n\t\t\tscript.onerror = () => {\n\t\t\t\t// If CDN fails, try local path\n\t\t\t\tconsole.info(\"[MCP Client] Trying local path for html2canvas...\");\n\t\t\t\tconst localScript = document.createElement(\"script\");\n\t\t\t\tlocalScript.src = \"/html2canvas.min.js\";\n\t\t\t\tlocalScript.onload = () => {\n\t\t\t\t\tconsole.info(\"[MCP Client] html2canvas loaded from local path\");\n\t\t\t\t\tresolve(true);\n\t\t\t\t};\n\t\t\t\tlocalScript.onerror = (err) => {\n\t\t\t\t\t// Try using unpkg CDN\n\t\t\t\t\tconsole.info(\"[MCP Client] Trying unpkg CDN for html2canvas...\");\n\t\t\t\t\tconst unpkgScript = document.createElement(\"script\");\n\t\t\t\t\tunpkgScript.src =\n\t\t\t\t\t\t\"https://unpkg.com/html2canvas/dist/html2canvas.min.js\";\n\t\t\t\t\tunpkgScript.onload = () => {\n\t\t\t\t\t\tconsole.info(\"[MCP Client] html2canvas loaded from unpkg CDN\");\n\t\t\t\t\t\tresolve(true);\n\t\t\t\t\t};\n\t\t\t\t\tunpkgScript.onerror = (err) => {\n\t\t\t\t\t\tconsole.error(\"[MCP Client] Failed to load html2canvas:\", err);\n\t\t\t\t\t\treject(\n\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t\"Unable to load html2canvas library, please ensure your network connection is working\",\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t};\n\t\t\t\t\tdocument.head.appendChild(unpkgScript);\n\t\t\t\t};\n\t\t\t\tdocument.head.appendChild(localScript);\n\t\t\t};\n\t\t\tdocument.head.appendChild(script);\n\t\t});\n\t}\n\n\t/**\n\t * Handle messages from server\n\t */\n\tprivate handleServerMessage(message: MCPMessage): void {\n\t\t// Handle special request types\n\t\tif (message.type === \"get_page_html\") {\n\t\t\t// Handle request to get page HTML\n\t\t\tconst requestId = message.requestId as string;\n\t\t\ttry {\n\t\t\t\tconst html = document.documentElement.outerHTML;\n\t\t\t\tthis.sendResponse(requestId, { html });\n\t\t\t} catch (error) {\n\t\t\t\tthis.sendError(\n\t\t\t\t\trequestId,\n\t\t\t\t\t`Error getting HTML: ${(error as Error).message}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.type === \"execute_js\") {\n\t\t\t// Handle request to execute JavaScript\n\t\t\tconst requestId = message.requestId as string;\n\t\t\tconst code = message.code as string;\n\t\t\ttry {\n\t\t\t\t// Use Function constructor to create function, then execute\n\t\t\t\tconst result = new Function(code)();\n\t\t\t\t// Ensure result can be JSON serialized\n\t\t\t\tlet serializedResult: string;\n\t\t\t\ttry {\n\t\t\t\t\tserializedResult = JSON.stringify(result);\n\t\t\t\t} catch (err) {\n\t\t\t\t\t// If result cannot be serialized, return string representation\n\t\t\t\t\tserializedResult = String(result);\n\t\t\t\t}\n\n\t\t\t\t// Send response\n\t\t\t\tconst response = {\n\t\t\t\t\trequestId,\n\t\t\t\t\tresult: serializedResult,\n\t\t\t\t};\n\n\t\t\t\tthis.ws?.send(JSON.stringify(response));\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"[MCP Client] Error executing JavaScript:\", error);\n\t\t\t\tthis.sendError(\n\t\t\t\t\trequestId,\n\t\t\t\t\t`Error executing JavaScript: ${(error as Error).message}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.type === \"get_page_title\") {\n\t\t\t// Handle request to get page title\n\t\t\tconst requestId = message.requestId as string;\n\t\t\ttry {\n\t\t\t\tconst title = document.title;\n\t\t\t\tthis.sendResponse(requestId, { title });\n\t\t\t} catch (error) {\n\t\t\t\tthis.sendError(\n\t\t\t\t\trequestId,\n\t\t\t\t\t`Error getting title: ${(error as Error).message}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.type === \"get_elements\") {\n\t\t\t// Handle request to get elements\n\t\t\tconst requestId = message.requestId as string;\n\t\t\tconst selector = message.selector as string;\n\t\t\ttry {\n\t\t\t\tconst elements = [...document.querySelectorAll(selector)];\n\t\t\t\tconst result = elements.map((el) => ({\n\t\t\t\t\ttagName: el.tagName,\n\t\t\t\t\tid: el.id,\n\t\t\t\t\tclassName: el.className,\n\t\t\t\t\ttextContent: el.textContent?.trim().substring(0, 500) || \"\",\n\t\t\t\t\tattributes: [...el.attributes].reduce(\n\t\t\t\t\t\t(attrs: Record<string, string>, attr) => {\n\t\t\t\t\t\t\tattrs[attr.name] = attr.value;\n\t\t\t\t\t\t\treturn attrs;\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{},\n\t\t\t\t\t),\n\t\t\t\t}));\n\t\t\t\tthis.sendResponse(requestId, { elements: result });\n\t\t\t} catch (error) {\n\t\t\t\tthis.sendError(\n\t\t\t\t\trequestId,\n\t\t\t\t\t`Error getting elements: ${(error as Error).message}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.type === \"capture_screenshot\") {\n\t\t\t// Handle screenshot request\n\t\t\tconst requestId = message.requestId as string;\n\t\t\tconst selector = (message.selector as string) || \"body\";\n\n\t\t\t// Process screenshot asynchronously\n\t\t\t(async () => {\n\t\t\t\ttry {\n\t\t\t\t\t// Ensure html2canvas is loaded\n\t\t\t\t\tawait this.loadHtml2Canvas();\n\n\t\t\t\t\tconst element = document.querySelector(selector);\n\t\t\t\t\tif (!element) {\n\t\t\t\t\t\tthis.sendError(requestId, `Element not found: ${selector}`);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Use html2canvas for screenshot, add config for better compatibility\n\t\t\t\t\tconst html2canvasOptions: Html2CanvasOptions = {\n\t\t\t\t\t\tallowTaint: true,\n\t\t\t\t\t\tuseCORS: true,\n\t\t\t\t\t\tlogging: false,\n\t\t\t\t\t\tscale: window.devicePixelRatio || 1,\n\t\t\t\t\t\tbackgroundColor: null, // Transparent background\n\t\t\t\t\t\tremoveContainer: true, // Remove temporary container\n\t\t\t\t\t\t// Calculate actual element size and position\n\t\t\t\t\t\tx: 0,\n\t\t\t\t\t\ty: 0,\n\t\t\t\t\t\tscrollX: 0,\n\t\t\t\t\t\tscrollY: 0,\n\t\t\t\t\t\t// Get actual element width and height\n\t\t\t\t\t\twidth: (element as HTMLElement).offsetWidth,\n\t\t\t\t\t\theight: (element as HTMLElement).offsetHeight,\n\t\t\t\t\t};\n\n\t\t\t\t\tconst html2canvas = (window as WindowWithHtml2Canvas).html2canvas;\n\n\t\t\t\t\tif (!html2canvas) {\n\t\t\t\t\t\tthis.sendError(\n\t\t\t\t\t\t\trequestId,\n\t\t\t\t\t\t\t\"html2canvas library not properly loaded\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tlet canvas = await html2canvas(\n\t\t\t\t\t\telement as HTMLElement,\n\t\t\t\t\t\thtml2canvasOptions,\n\t\t\t\t\t);\n\n\t\t\t\t\tif (!canvas) {\n\t\t\t\t\t\tthis.sendError(\n\t\t\t\t\t\t\trequestId,\n\t\t\t\t\t\t\t\"Screenshot failed: unable to create canvas\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Crop canvas, remove excess white space\n\t\t\t\t\tconst context = canvas.getContext(\"2d\");\n\t\t\t\t\tif (context) {\n\t\t\t\t\t\t// Try to detect content area, remove whitespace\n\t\t\t\t\t\tconst imageData = context.getImageData(\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\tcanvas.width,\n\t\t\t\t\t\t\tcanvas.height,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst bounds = this.getContentBounds(imageData);\n\n\t\t\t\t\t\tif (bounds) {\n\t\t\t\t\t\t\t// If content boundaries found, create a new cropped canvas\n\t\t\t\t\t\t\tconst croppedCanvas = document.createElement(\"canvas\");\n\t\t\t\t\t\t\tcroppedCanvas.width = bounds.width;\n\t\t\t\t\t\t\tcroppedCanvas.height = bounds.height;\n\n\t\t\t\t\t\t\tconst croppedContext = croppedCanvas.getContext(\"2d\");\n\t\t\t\t\t\t\tif (croppedContext) {\n\t\t\t\t\t\t\t\tcroppedContext.drawImage(\n\t\t\t\t\t\t\t\t\tcanvas,\n\t\t\t\t\t\t\t\t\tbounds.left,\n\t\t\t\t\t\t\t\t\tbounds.top,\n\t\t\t\t\t\t\t\t\tbounds.width,\n\t\t\t\t\t\t\t\t\tbounds.height,\n\t\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t\t\tbounds.width,\n\t\t\t\t\t\t\t\t\tbounds.height,\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\t// Use cropped canvas\n\t\t\t\t\t\t\t\tcanvas = croppedCanvas;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Compress image quality to reduce data size, use PNG format to maintain transparency\n\t\t\t\t\tconst dataUrl = canvas.toDataURL(\"image/png\");\n\t\t\t\t\tthis.sendResponse(requestId, { imageDataUrl: dataUrl });\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error(\"[MCP Client] Screenshot error:\", error);\n\t\t\t\t\tthis.sendError(\n\t\t\t\t\t\trequestId,\n\t\t\t\t\t\t`Screenshot error: ${(error as Error).message}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t})();\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.type === \"get_page_url\") {\n\t\t\t// Handle request to get page URL\n\t\t\tconst requestId = message.requestId as string;\n\t\t\ttry {\n\t\t\t\tconst url = window.location.href;\n\t\t\t\tthis.sendResponse(requestId, { url });\n\t\t\t} catch (error) {\n\t\t\t\tthis.sendError(\n\t\t\t\t\trequestId,\n\t\t\t\t\t`Error getting URL: ${(error as Error).message}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.type === \"click_element\") {\n\t\t\t// Handle request to click element\n\t\t\tconst requestId = message.requestId as string;\n\t\t\tconst selector = message.selector as string;\n\t\t\ttry {\n\t\t\t\tconst element = document.querySelector(selector);\n\t\t\t\tif (!element) {\n\t\t\t\t\tthis.sendError(requestId, `Element not found: ${selector}`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t(element as HTMLElement).click();\n\t\t\t\tthis.sendResponse(requestId, {\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tmessage: `Successfully clicked element: ${selector}`,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tthis.sendError(\n\t\t\t\t\trequestId,\n\t\t\t\t\t`Error clicking element: ${(error as Error).message}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.type === \"input_text\") {\n\t\t\t// Handle request to input text\n\t\t\tconst requestId = message.requestId as string;\n\t\t\tconst selector = message.selector as string;\n\t\t\tconst text = message.text as string;\n\n\t\t\ttry {\n\t\t\t\tconst input = document.querySelector(selector);\n\t\t\t\tif (!input) {\n\t\t\t\t\tthis.sendError(requestId, `Input field not found: ${selector}`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (input.tagName !== \"INPUT\" && input.tagName !== \"TEXTAREA\") {\n\t\t\t\t\tthis.sendError(\n\t\t\t\t\t\trequestId,\n\t\t\t\t\t\t`Selected element is not an input field: ${input.tagName}`,\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t(input as HTMLInputElement).value = text;\n\t\t\t\tinput.dispatchEvent(new Event(\"input\", { bubbles: true }));\n\n\t\t\t\tthis.sendResponse(requestId, {\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tmessage: `Successfully input text to: ${selector}`,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tthis.sendError(\n\t\t\t\t\trequestId,\n\t\t\t\t\t`Error inputting text: ${(error as Error).message}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Handle other message types\n\t\tswitch (message.type) {\n\t\t\tcase \"command_result\":\n\t\t\t\tconsole.info(\n\t\t\t\t\t`%c[MCP Server] ${message.payload?.result}`,\n\t\t\t\t\t\"color: blue\",\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase \"error\":\n\t\t\t\tconsole.error(`[MCP Server] Error: ${message.payload?.error}`);\n\t\t\t\tbreak;\n\t\t\tcase \"connection_status\":\n\t\t\t\tconsole.info(`%c[MCP Server] ${message.message}`, \"color: green\");\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t// Only log message type, not full message content\n\t\t\t\tconsole.info(`[MCP Server] Received message type: ${message.type}`);\n\t\t}\n\t}\n\n\t/**\n\t * Send response\n\t */\n\tprivate sendResponse(requestId: string, data: Record<string, unknown>): void {\n\t\tif (!this.connected || !this.ws) {\n\t\t\tconsole.error(\n\t\t\t\t\"[MCP Client] Not connected to server, cannot send response\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst response = {\n\t\t\trequestId,\n\t\t\t...data,\n\t\t};\n\n\t\tthis.ws.send(JSON.stringify(response));\n\t}\n\n\t/**\n\t * Send error response\n\t */\n\tprivate sendError(requestId: string, errorMessage: string): void {\n\t\tif (!this.connected || !this.ws) {\n\t\t\tconsole.error(\n\t\t\t\t\"[MCP Client] Not connected to server, cannot send error response\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst response = {\n\t\t\trequestId,\n\t\t\terror: errorMessage,\n\t\t};\n\n\t\tthis.ws.send(JSON.stringify(response));\n\t}\n\n\t/**\n\t * Send command to server\n\t */\n\tsendCommand(command: string): void {\n\t\tif (!this.connected || !this.ws) {\n\t\t\tconsole.error(\"[MCP Client] Not connected to server\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst message: MCPMessage = {\n\t\t\ttype: \"command\",\n\t\t\tpayload: { command },\n\t\t};\n\n\t\tthis.ws.send(JSON.stringify(message));\n\t\tthis.commandHistory.push(command);\n\t\tthis.historyIndex = this.commandHistory.length;\n\t}\n\n\t/**\n\t * Register console commands\n\t */\n\tprivate registerConsoleCommands(): void {\n\t\t// Define global commands\n\t\twindow.mcp = {\n\t\t\texec: (command: string) => {\n\t\t\t\tthis.sendCommand(command);\n\t\t\t\treturn \"Command sent\";\n\t\t\t},\n\t\t\tdisconnect: () => {\n\t\t\t\tif (this.ws) {\n\t\t\t\t\tthis.ws.close();\n\t\t\t\t\tthis.ws = null;\n\t\t\t\t}\n\t\t\t\treturn \"Disconnected\";\n\t\t\t},\n\t\t\treconnect: () => {\n\t\t\t\tthis.connect();\n\t\t\t\treturn \"Reconnecting...\";\n\t\t\t},\n\t\t\thelp: () => {\n\t\t\t\treturn `\nMCP Client Commands:\n mcp.exec(command) - Execute a command\n mcp.disconnect() - Disconnect from server\n mcp.reconnect() - Reconnect to server\n mcp.help() - Show help information\n `;\n\t\t\t},\n\t\t};\n\n\t\tconsole.info(\n\t\t\t\"%c[MCP Client] Console commands registered, use mcp.help() to see available commands\",\n\t\t\t\"color: green\",\n\t\t);\n\t}\n\n\t/**\n\t * Get image content boundaries, remove excess whitespace\n\t */\n\tprivate getContentBounds(\n\t\timageData: ImageData,\n\t): { left: number; top: number; width: number; height: number } | null {\n\t\tconst { width, height, data } = imageData;\n\t\tlet minX = width;\n\t\tlet minY = height;\n\t\tlet maxX = 0;\n\t\tlet maxY = 0;\n\t\tlet hasContent = false;\n\n\t\t// Iterate through pixel data, find boundaries of non-transparent pixels\n\t\tfor (let y = 0; y < height; y++) {\n\t\t\tfor (let x = 0; x < width; x++) {\n\t\t\t\tconst alpha = data[(y * width + x) * 4 + 3]; // Alpha channel\n\t\t\t\tif (alpha > 10) {\n\t\t\t\t\t// Non-transparent pixel (allowing some slight transparency)\n\t\t\t\t\thasContent = true;\n\t\t\t\t\tminX = Math.min(minX, x);\n\t\t\t\t\tminY = Math.min(minY, y);\n\t\t\t\t\tmaxX = Math.max(maxX, x);\n\t\t\t\t\tmaxY = Math.max(maxY, y);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!hasContent) {\n\t\t\treturn null; // No content found\n\t\t}\n\n\t\t// Add some padding\n\t\tconst padding = 10;\n\t\tminX = Math.max(0, minX - padding);\n\t\tminY = Math.max(0, minY - padding);\n\t\tmaxX = Math.min(width - 1, maxX + padding);\n\t\tmaxY = Math.min(height - 1, maxY + padding);\n\n\t\treturn {\n\t\t\tleft: minX,\n\t\t\ttop: minY,\n\t\t\twidth: maxX - minX + 1,\n\t\t\theight: maxY - minY + 1,\n\t\t};\n\t}\n}\n\n// Create and connect client instance\nconst client = new BrowserConsoleMCP();\nclient.connect();\n\n// Export client instance\nexport default client;\n"],"names":["client","constructor","serverUrl","this","ws","connected","commandHistory","historyIndex","connect","loadHtml2Canvas","then","WebSocket","onopen","console","info","registerConsoleCommands","onmessage","event","message","JSON","parse","data","handleServerMessage","error","onclose","onerror","catch","Promise","resolve","reject","window","html2canvas","script","document","createElement","src","onload","localScript","err","unpkgScript","Error","head","appendChild","type","payload","result","requestId","selector","text","input","querySelector","sendError","tagName","value","dispatchEvent","Event","bubbles","sendResponse","success","element","click","url","location","href","html2canvasOptions","allowTaint","useCORS","logging","scale","devicePixelRatio","backgroundColor","removeContainer","x","y","scrollX","scrollY","width","offsetWidth","height","offsetHeight","canvas","context","getContext","imageData","getImageData","bounds","getContentBounds","croppedCanvas","croppedContext","drawImage","left","top","dataUrl","toDataURL","imageDataUrl","querySelectorAll","map","el","id","className","textContent","trim","substring","attributes","reduce","attrs","attr","name","elements","title","code","Function","serializedResult","stringify","String","response","send","html","documentElement","outerHTML","errorMessage","sendCommand","command","push","length","mcp","exec","disconnect","close","reconnect","help","minX","minY","maxX","maxY","hasContent","Math","min","max"],"mappings":"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"}
|
package/rollup.browser.config.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import commonjs from "@rollup/plugin-commonjs";
|
|
2
|
-
import resolve from "@rollup/plugin-node-resolve";
|
|
3
|
-
import typescript from "@rollup/plugin-typescript";
|
|
4
|
-
import { terser } from "rollup-plugin-terser";
|
|
5
|
-
|
|
6
|
-
export default {
|
|
7
|
-
input: "src/browser/index.ts",
|
|
8
|
-
output: {
|
|
9
|
-
file: "dist/browser/index.js",
|
|
10
|
-
format: "es",
|
|
11
|
-
sourcemap: true,
|
|
12
|
-
},
|
|
13
|
-
plugins: [
|
|
14
|
-
resolve(),
|
|
15
|
-
commonjs(),
|
|
16
|
-
typescript({
|
|
17
|
-
tsconfig: "./tsconfig.browser.json",
|
|
18
|
-
sourceMap: true,
|
|
19
|
-
inlineSources: true,
|
|
20
|
-
}),
|
|
21
|
-
terser(),
|
|
22
|
-
],
|
|
23
|
-
};
|
package/rollup.config.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import commonjs from "@rollup/plugin-commonjs";
|
|
2
|
-
import resolve from "@rollup/plugin-node-resolve";
|
|
3
|
-
import typescript from "@rollup/plugin-typescript";
|
|
4
|
-
import { terser } from "rollup-plugin-terser";
|
|
5
|
-
|
|
6
|
-
export default {
|
|
7
|
-
input: "src/client/index.ts",
|
|
8
|
-
output: {
|
|
9
|
-
file: "dist/client/browser-console-mcp.js",
|
|
10
|
-
format: "iife",
|
|
11
|
-
name: "BrowserConsoleMCP",
|
|
12
|
-
sourcemap: true,
|
|
13
|
-
},
|
|
14
|
-
plugins: [
|
|
15
|
-
resolve(),
|
|
16
|
-
commonjs(),
|
|
17
|
-
typescript({
|
|
18
|
-
tsconfig: "./tsconfig.client.json",
|
|
19
|
-
}),
|
|
20
|
-
terser(),
|
|
21
|
-
],
|
|
22
|
-
};
|