hypha-debugger 0.1.10 → 0.2.0
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/dist/hypha-debugger.js +3812 -329
- package/dist/hypha-debugger.js.map +1 -1
- package/dist/hypha-debugger.min.js +21 -12
- package/dist/hypha-debugger.min.js.map +1 -1
- package/dist/hypha-debugger.mjs +98 -38
- package/dist/hypha-debugger.mjs.map +1 -1
- package/dist/services/screenshot.d.ts +5 -4
- package/package.json +8 -8
package/dist/hypha-debugger.mjs
CHANGED
|
@@ -1968,27 +1968,95 @@ async function toJpeg(node, options = {}) {
|
|
|
1968
1968
|
|
|
1969
1969
|
/**
|
|
1970
1970
|
* Screenshot capture service using html-to-image.
|
|
1971
|
+
*
|
|
1972
|
+
* Images are downscaled before being returned so agents don't receive
|
|
1973
|
+
* multi-megabyte base64 payloads that can crash their context window.
|
|
1971
1974
|
*/
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1975
|
+
/**
|
|
1976
|
+
* Resize an image data URL via a canvas. Returns a new data URL at the
|
|
1977
|
+
* requested format/quality. Maintains aspect ratio: fits within
|
|
1978
|
+
* (maxWidth × maxHeight) without distortion.
|
|
1979
|
+
*/
|
|
1980
|
+
async function resizeDataUrl(dataUrl, maxWidth, maxHeight, format, quality) {
|
|
1981
|
+
return new Promise((resolve, reject) => {
|
|
1982
|
+
const img = new Image();
|
|
1983
|
+
img.onload = () => {
|
|
1984
|
+
const srcW = img.naturalWidth;
|
|
1985
|
+
const srcH = img.naturalHeight;
|
|
1986
|
+
// Compute scale to fit within bounds (but never upscale)
|
|
1987
|
+
const scale = Math.min(maxWidth / srcW, maxHeight / srcH, 1);
|
|
1988
|
+
const dstW = Math.max(1, Math.round(srcW * scale));
|
|
1989
|
+
const dstH = Math.max(1, Math.round(srcH * scale));
|
|
1990
|
+
const canvas = document.createElement("canvas");
|
|
1991
|
+
canvas.width = dstW;
|
|
1992
|
+
canvas.height = dstH;
|
|
1993
|
+
const ctx = canvas.getContext("2d");
|
|
1994
|
+
if (!ctx) {
|
|
1995
|
+
reject(new Error("Could not get 2D canvas context"));
|
|
1996
|
+
return;
|
|
1997
|
+
}
|
|
1998
|
+
// Fill white background for JPEG (no alpha support)
|
|
1999
|
+
if (format === "jpeg") {
|
|
2000
|
+
ctx.fillStyle = "#ffffff";
|
|
2001
|
+
ctx.fillRect(0, 0, dstW, dstH);
|
|
2002
|
+
}
|
|
2003
|
+
ctx.drawImage(img, 0, 0, dstW, dstH);
|
|
2004
|
+
const mime = format === "jpeg" ? "image/jpeg" : "image/png";
|
|
2005
|
+
const out = canvas.toDataURL(mime, quality);
|
|
2006
|
+
resolve({ dataUrl: out, width: dstW, height: dstH });
|
|
2007
|
+
};
|
|
2008
|
+
img.onerror = () => reject(new Error("Failed to load image for resizing"));
|
|
2009
|
+
img.src = dataUrl;
|
|
2010
|
+
});
|
|
2011
|
+
}
|
|
2012
|
+
async function takeScreenshot(selector, format, quality, max_width, max_height, full_page) {
|
|
2013
|
+
// Agent-friendly defaults: JPEG, moderate quality, capped at 1024px,
|
|
2014
|
+
// viewport-only (not the entire scrollable page).
|
|
2015
|
+
const fmt = format ?? "jpeg";
|
|
2016
|
+
const qual = quality ?? 0.75;
|
|
2017
|
+
const maxW = max_width ?? 1024;
|
|
2018
|
+
const maxH = max_height ?? 1024;
|
|
2019
|
+
const capturePage = full_page ?? false;
|
|
2020
|
+
// Pick target:
|
|
2021
|
+
// - explicit selector → that element
|
|
2022
|
+
// - full_page=true → document.documentElement (the entire scrollable page)
|
|
2023
|
+
// - default → viewport-sized region (clipped to window size)
|
|
2024
|
+
let target;
|
|
2025
|
+
if (selector) {
|
|
2026
|
+
target = document.querySelector(selector);
|
|
2027
|
+
if (!target) {
|
|
2028
|
+
return { error: `No element found for selector: ${selector}` };
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
else if (capturePage) {
|
|
2032
|
+
target = document.documentElement;
|
|
2033
|
+
}
|
|
2034
|
+
else {
|
|
2035
|
+
target = document.body;
|
|
1979
2036
|
}
|
|
1980
2037
|
try {
|
|
1981
2038
|
const node = target;
|
|
2039
|
+
// For viewport-only captures, limit html-to-image's output size
|
|
2040
|
+
// to the viewport dimensions.
|
|
2041
|
+
const viewportW = window.innerWidth;
|
|
2042
|
+
const viewportH = window.innerHeight;
|
|
1982
2043
|
const captureOptions = {
|
|
1983
2044
|
quality: qual,
|
|
1984
|
-
pixelRatio:
|
|
2045
|
+
pixelRatio: 1, // always capture at 1x — we'll resize after
|
|
1985
2046
|
cacheBust: true,
|
|
1986
2047
|
skipAutoScale: true,
|
|
1987
|
-
// Filter out the debugger overlay itself
|
|
1988
2048
|
filter: (el) => {
|
|
1989
|
-
|
|
2049
|
+
// Exclude the debugger overlay and cursor from screenshots
|
|
2050
|
+
return (el.id !== "hypha-debugger-host" &&
|
|
2051
|
+
el.id !== "hypha-debugger-cursor" &&
|
|
2052
|
+
el.id !== "playwright-highlight-container");
|
|
1990
2053
|
},
|
|
1991
2054
|
};
|
|
2055
|
+
if (!selector && !capturePage) {
|
|
2056
|
+
// Viewport-only capture: constrain canvas to window size
|
|
2057
|
+
captureOptions.width = viewportW;
|
|
2058
|
+
captureOptions.height = viewportH;
|
|
2059
|
+
}
|
|
1992
2060
|
let dataUrl;
|
|
1993
2061
|
if (fmt === "jpeg") {
|
|
1994
2062
|
dataUrl = await toJpeg(node, captureOptions);
|
|
@@ -1996,26 +2064,15 @@ async function takeScreenshot(selector, format, quality, scale, max_width, max_h
|
|
|
1996
2064
|
else {
|
|
1997
2065
|
dataUrl = await toPng(node, captureOptions);
|
|
1998
2066
|
}
|
|
1999
|
-
//
|
|
2000
|
-
const
|
|
2001
|
-
|
|
2002
|
-
let height = Math.round(rect.height * scl);
|
|
2003
|
-
// Optionally resize if too large
|
|
2004
|
-
if (max_width && width > max_width) {
|
|
2005
|
-
const ratio = max_width / width;
|
|
2006
|
-
width = max_width;
|
|
2007
|
-
height = Math.round(height * ratio);
|
|
2008
|
-
}
|
|
2009
|
-
if (max_height && height > max_height) {
|
|
2010
|
-
const ratio = max_height / height;
|
|
2011
|
-
height = max_height;
|
|
2012
|
-
width = Math.round(width * ratio);
|
|
2013
|
-
}
|
|
2067
|
+
// Resize down to fit within (maxW × maxH) and re-encode
|
|
2068
|
+
const resized = await resizeDataUrl(dataUrl, maxW, maxH, fmt, qual);
|
|
2069
|
+
const sizeKb = Math.round((resized.dataUrl.length * 0.75) / 1024); // rough base64 → bytes
|
|
2014
2070
|
return {
|
|
2015
|
-
data: dataUrl,
|
|
2071
|
+
data: resized.dataUrl,
|
|
2016
2072
|
format: fmt,
|
|
2017
|
-
width,
|
|
2018
|
-
height,
|
|
2073
|
+
width: resized.width,
|
|
2074
|
+
height: resized.height,
|
|
2075
|
+
size_kb: sizeKb,
|
|
2019
2076
|
};
|
|
2020
2077
|
}
|
|
2021
2078
|
catch (err) {
|
|
@@ -2024,34 +2081,37 @@ async function takeScreenshot(selector, format, quality, scale, max_width, max_h
|
|
|
2024
2081
|
}
|
|
2025
2082
|
takeScreenshot.__schema__ = {
|
|
2026
2083
|
name: "takeScreenshot",
|
|
2027
|
-
description: "Capture a screenshot of the
|
|
2084
|
+
description: "Capture a screenshot of the current viewport, a specific element, or the full page. " +
|
|
2085
|
+
"Returns a base64-encoded data URL, downscaled to fit within max_width × max_height " +
|
|
2086
|
+
"(default 1024px) to keep the payload small enough for AI agents. Defaults to JPEG " +
|
|
2087
|
+
"format at 0.75 quality for reasonable file size.",
|
|
2028
2088
|
parameters: {
|
|
2029
2089
|
type: "object",
|
|
2030
2090
|
properties: {
|
|
2031
2091
|
selector: {
|
|
2032
2092
|
type: "string",
|
|
2033
|
-
description: "CSS selector of the element to capture. Omit to capture the
|
|
2093
|
+
description: "CSS selector of the element to capture. Omit to capture the viewport (or full page if full_page=true).",
|
|
2034
2094
|
},
|
|
2035
2095
|
format: {
|
|
2036
2096
|
type: "string",
|
|
2037
2097
|
enum: ["png", "jpeg"],
|
|
2038
|
-
description: 'Image format. Default: "png".',
|
|
2098
|
+
description: 'Image format. Default: "jpeg" (much smaller than PNG). Use "png" for sharp text.',
|
|
2039
2099
|
},
|
|
2040
2100
|
quality: {
|
|
2041
2101
|
type: "number",
|
|
2042
|
-
description: "
|
|
2043
|
-
},
|
|
2044
|
-
scale: {
|
|
2045
|
-
type: "number",
|
|
2046
|
-
description: "Pixel ratio / scale factor. Default: 1. Use 2 for retina.",
|
|
2102
|
+
description: "JPEG quality (0–1). Default: 0.75. Ignored for PNG. Lower = smaller payload.",
|
|
2047
2103
|
},
|
|
2048
2104
|
max_width: {
|
|
2049
2105
|
type: "number",
|
|
2050
|
-
description: "Maximum width in pixels. Image
|
|
2106
|
+
description: "Maximum output width in pixels. Default: 1024. Image is scaled down preserving aspect ratio.",
|
|
2051
2107
|
},
|
|
2052
2108
|
max_height: {
|
|
2053
2109
|
type: "number",
|
|
2054
|
-
description: "Maximum height in pixels. Image
|
|
2110
|
+
description: "Maximum output height in pixels. Default: 1024. Image is scaled down preserving aspect ratio.",
|
|
2111
|
+
},
|
|
2112
|
+
full_page: {
|
|
2113
|
+
type: "boolean",
|
|
2114
|
+
description: "If true, capture the entire scrollable page instead of just the viewport. Default: false.",
|
|
2055
2115
|
},
|
|
2056
2116
|
},
|
|
2057
2117
|
},
|