hypha-debugger 0.1.10 → 0.1.11
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 +98 -38
- package/dist/hypha-debugger.js.map +1 -1
- package/dist/hypha-debugger.min.js +2 -2
- 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 +1 -1
package/dist/hypha-debugger.js
CHANGED
|
@@ -10425,27 +10425,95 @@
|
|
|
10425
10425
|
|
|
10426
10426
|
/**
|
|
10427
10427
|
* Screenshot capture service using html-to-image.
|
|
10428
|
+
*
|
|
10429
|
+
* Images are downscaled before being returned so agents don't receive
|
|
10430
|
+
* multi-megabyte base64 payloads that can crash their context window.
|
|
10428
10431
|
*/
|
|
10429
|
-
|
|
10430
|
-
|
|
10431
|
-
|
|
10432
|
-
|
|
10433
|
-
|
|
10434
|
-
|
|
10435
|
-
|
|
10432
|
+
/**
|
|
10433
|
+
* Resize an image data URL via a canvas. Returns a new data URL at the
|
|
10434
|
+
* requested format/quality. Maintains aspect ratio: fits within
|
|
10435
|
+
* (maxWidth × maxHeight) without distortion.
|
|
10436
|
+
*/
|
|
10437
|
+
async function resizeDataUrl(dataUrl, maxWidth, maxHeight, format, quality) {
|
|
10438
|
+
return new Promise((resolve, reject) => {
|
|
10439
|
+
const img = new Image();
|
|
10440
|
+
img.onload = () => {
|
|
10441
|
+
const srcW = img.naturalWidth;
|
|
10442
|
+
const srcH = img.naturalHeight;
|
|
10443
|
+
// Compute scale to fit within bounds (but never upscale)
|
|
10444
|
+
const scale = Math.min(maxWidth / srcW, maxHeight / srcH, 1);
|
|
10445
|
+
const dstW = Math.max(1, Math.round(srcW * scale));
|
|
10446
|
+
const dstH = Math.max(1, Math.round(srcH * scale));
|
|
10447
|
+
const canvas = document.createElement("canvas");
|
|
10448
|
+
canvas.width = dstW;
|
|
10449
|
+
canvas.height = dstH;
|
|
10450
|
+
const ctx = canvas.getContext("2d");
|
|
10451
|
+
if (!ctx) {
|
|
10452
|
+
reject(new Error("Could not get 2D canvas context"));
|
|
10453
|
+
return;
|
|
10454
|
+
}
|
|
10455
|
+
// Fill white background for JPEG (no alpha support)
|
|
10456
|
+
if (format === "jpeg") {
|
|
10457
|
+
ctx.fillStyle = "#ffffff";
|
|
10458
|
+
ctx.fillRect(0, 0, dstW, dstH);
|
|
10459
|
+
}
|
|
10460
|
+
ctx.drawImage(img, 0, 0, dstW, dstH);
|
|
10461
|
+
const mime = format === "jpeg" ? "image/jpeg" : "image/png";
|
|
10462
|
+
const out = canvas.toDataURL(mime, quality);
|
|
10463
|
+
resolve({ dataUrl: out, width: dstW, height: dstH });
|
|
10464
|
+
};
|
|
10465
|
+
img.onerror = () => reject(new Error("Failed to load image for resizing"));
|
|
10466
|
+
img.src = dataUrl;
|
|
10467
|
+
});
|
|
10468
|
+
}
|
|
10469
|
+
async function takeScreenshot(selector, format, quality, max_width, max_height, full_page) {
|
|
10470
|
+
// Agent-friendly defaults: JPEG, moderate quality, capped at 1024px,
|
|
10471
|
+
// viewport-only (not the entire scrollable page).
|
|
10472
|
+
const fmt = format ?? "jpeg";
|
|
10473
|
+
const qual = quality ?? 0.75;
|
|
10474
|
+
const maxW = max_width ?? 1024;
|
|
10475
|
+
const maxH = max_height ?? 1024;
|
|
10476
|
+
const capturePage = full_page ?? false;
|
|
10477
|
+
// Pick target:
|
|
10478
|
+
// - explicit selector → that element
|
|
10479
|
+
// - full_page=true → document.documentElement (the entire scrollable page)
|
|
10480
|
+
// - default → viewport-sized region (clipped to window size)
|
|
10481
|
+
let target;
|
|
10482
|
+
if (selector) {
|
|
10483
|
+
target = document.querySelector(selector);
|
|
10484
|
+
if (!target) {
|
|
10485
|
+
return { error: `No element found for selector: ${selector}` };
|
|
10486
|
+
}
|
|
10487
|
+
}
|
|
10488
|
+
else if (capturePage) {
|
|
10489
|
+
target = document.documentElement;
|
|
10490
|
+
}
|
|
10491
|
+
else {
|
|
10492
|
+
target = document.body;
|
|
10436
10493
|
}
|
|
10437
10494
|
try {
|
|
10438
10495
|
const node = target;
|
|
10496
|
+
// For viewport-only captures, limit html-to-image's output size
|
|
10497
|
+
// to the viewport dimensions.
|
|
10498
|
+
const viewportW = window.innerWidth;
|
|
10499
|
+
const viewportH = window.innerHeight;
|
|
10439
10500
|
const captureOptions = {
|
|
10440
10501
|
quality: qual,
|
|
10441
|
-
pixelRatio:
|
|
10502
|
+
pixelRatio: 1, // always capture at 1x — we'll resize after
|
|
10442
10503
|
cacheBust: true,
|
|
10443
10504
|
skipAutoScale: true,
|
|
10444
|
-
// Filter out the debugger overlay itself
|
|
10445
10505
|
filter: (el) => {
|
|
10446
|
-
|
|
10506
|
+
// Exclude the debugger overlay and cursor from screenshots
|
|
10507
|
+
return (el.id !== "hypha-debugger-host" &&
|
|
10508
|
+
el.id !== "hypha-debugger-cursor" &&
|
|
10509
|
+
el.id !== "playwright-highlight-container");
|
|
10447
10510
|
},
|
|
10448
10511
|
};
|
|
10512
|
+
if (!selector && !capturePage) {
|
|
10513
|
+
// Viewport-only capture: constrain canvas to window size
|
|
10514
|
+
captureOptions.width = viewportW;
|
|
10515
|
+
captureOptions.height = viewportH;
|
|
10516
|
+
}
|
|
10449
10517
|
let dataUrl;
|
|
10450
10518
|
if (fmt === "jpeg") {
|
|
10451
10519
|
dataUrl = await toJpeg(node, captureOptions);
|
|
@@ -10453,26 +10521,15 @@
|
|
|
10453
10521
|
else {
|
|
10454
10522
|
dataUrl = await toPng(node, captureOptions);
|
|
10455
10523
|
}
|
|
10456
|
-
//
|
|
10457
|
-
const
|
|
10458
|
-
|
|
10459
|
-
let height = Math.round(rect.height * scl);
|
|
10460
|
-
// Optionally resize if too large
|
|
10461
|
-
if (max_width && width > max_width) {
|
|
10462
|
-
const ratio = max_width / width;
|
|
10463
|
-
width = max_width;
|
|
10464
|
-
height = Math.round(height * ratio);
|
|
10465
|
-
}
|
|
10466
|
-
if (max_height && height > max_height) {
|
|
10467
|
-
const ratio = max_height / height;
|
|
10468
|
-
height = max_height;
|
|
10469
|
-
width = Math.round(width * ratio);
|
|
10470
|
-
}
|
|
10524
|
+
// Resize down to fit within (maxW × maxH) and re-encode
|
|
10525
|
+
const resized = await resizeDataUrl(dataUrl, maxW, maxH, fmt, qual);
|
|
10526
|
+
const sizeKb = Math.round((resized.dataUrl.length * 0.75) / 1024); // rough base64 → bytes
|
|
10471
10527
|
return {
|
|
10472
|
-
data: dataUrl,
|
|
10528
|
+
data: resized.dataUrl,
|
|
10473
10529
|
format: fmt,
|
|
10474
|
-
width,
|
|
10475
|
-
height,
|
|
10530
|
+
width: resized.width,
|
|
10531
|
+
height: resized.height,
|
|
10532
|
+
size_kb: sizeKb,
|
|
10476
10533
|
};
|
|
10477
10534
|
}
|
|
10478
10535
|
catch (err) {
|
|
@@ -10481,34 +10538,37 @@
|
|
|
10481
10538
|
}
|
|
10482
10539
|
takeScreenshot.__schema__ = {
|
|
10483
10540
|
name: "takeScreenshot",
|
|
10484
|
-
description: "Capture a screenshot of the
|
|
10541
|
+
description: "Capture a screenshot of the current viewport, a specific element, or the full page. " +
|
|
10542
|
+
"Returns a base64-encoded data URL, downscaled to fit within max_width × max_height " +
|
|
10543
|
+
"(default 1024px) to keep the payload small enough for AI agents. Defaults to JPEG " +
|
|
10544
|
+
"format at 0.75 quality for reasonable file size.",
|
|
10485
10545
|
parameters: {
|
|
10486
10546
|
type: "object",
|
|
10487
10547
|
properties: {
|
|
10488
10548
|
selector: {
|
|
10489
10549
|
type: "string",
|
|
10490
|
-
description: "CSS selector of the element to capture. Omit to capture the
|
|
10550
|
+
description: "CSS selector of the element to capture. Omit to capture the viewport (or full page if full_page=true).",
|
|
10491
10551
|
},
|
|
10492
10552
|
format: {
|
|
10493
10553
|
type: "string",
|
|
10494
10554
|
enum: ["png", "jpeg"],
|
|
10495
|
-
description: 'Image format. Default: "png".',
|
|
10555
|
+
description: 'Image format. Default: "jpeg" (much smaller than PNG). Use "png" for sharp text.',
|
|
10496
10556
|
},
|
|
10497
10557
|
quality: {
|
|
10498
10558
|
type: "number",
|
|
10499
|
-
description: "
|
|
10500
|
-
},
|
|
10501
|
-
scale: {
|
|
10502
|
-
type: "number",
|
|
10503
|
-
description: "Pixel ratio / scale factor. Default: 1. Use 2 for retina.",
|
|
10559
|
+
description: "JPEG quality (0–1). Default: 0.75. Ignored for PNG. Lower = smaller payload.",
|
|
10504
10560
|
},
|
|
10505
10561
|
max_width: {
|
|
10506
10562
|
type: "number",
|
|
10507
|
-
description: "Maximum width in pixels. Image
|
|
10563
|
+
description: "Maximum output width in pixels. Default: 1024. Image is scaled down preserving aspect ratio.",
|
|
10508
10564
|
},
|
|
10509
10565
|
max_height: {
|
|
10510
10566
|
type: "number",
|
|
10511
|
-
description: "Maximum height in pixels. Image
|
|
10567
|
+
description: "Maximum output height in pixels. Default: 1024. Image is scaled down preserving aspect ratio.",
|
|
10568
|
+
},
|
|
10569
|
+
full_page: {
|
|
10570
|
+
type: "boolean",
|
|
10571
|
+
description: "If true, capture the entire scrollable page instead of just the viewport. Default: false.",
|
|
10512
10572
|
},
|
|
10513
10573
|
},
|
|
10514
10574
|
},
|