@skrillex1224/playwright-toolkit 2.1.226 → 2.1.228
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/README.md +2 -0
- package/dist/browser.js.map +2 -2
- package/dist/index.cjs +623 -93
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +623 -93
- package/dist/index.js.map +4 -4
- package/index.d.ts +39 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -466,6 +466,9 @@ function createInternalLogger(moduleName, explicitLogger) {
|
|
|
466
466
|
};
|
|
467
467
|
}
|
|
468
468
|
|
|
469
|
+
// src/internals/screenshot.js
|
|
470
|
+
var import_delay = __toESM(require("delay"), 1);
|
|
471
|
+
|
|
469
472
|
// src/internals/viewport.js
|
|
470
473
|
var toPositiveInt = (value) => {
|
|
471
474
|
const number = Math.round(Number(value) || 0);
|
|
@@ -499,11 +502,18 @@ var DEFAULT_TIMEOUT_MS = 5e3;
|
|
|
499
502
|
var FORCED_FULLPAGE_TYPE = "jpeg";
|
|
500
503
|
var FORCED_FULLPAGE_QUALITY = 50;
|
|
501
504
|
var SUPPORTED_TYPES = /* @__PURE__ */ new Set(["png", "jpeg", "webp"]);
|
|
505
|
+
var EXPANDED_SCROLLABLE_CLASS = "__pk_expanded__";
|
|
506
|
+
var DEFAULT_MAX_HEIGHT = 8e3;
|
|
507
|
+
var DEFAULT_SETTLE_MS = 1e3;
|
|
502
508
|
var toPositiveNumber = (value, fallback = 0) => {
|
|
503
509
|
const n = Number(value);
|
|
504
510
|
if (!Number.isFinite(n) || n <= 0) return fallback;
|
|
505
511
|
return n;
|
|
506
512
|
};
|
|
513
|
+
var toPositiveInteger = (value, fallback = 0) => {
|
|
514
|
+
const number = Math.round(Number(value) || 0);
|
|
515
|
+
return number > 0 ? number : fallback;
|
|
516
|
+
};
|
|
507
517
|
var normalizeType = (value) => {
|
|
508
518
|
const raw = String(value || "png").trim().toLowerCase();
|
|
509
519
|
if (!SUPPORTED_TYPES.has(raw)) return "png";
|
|
@@ -532,6 +542,138 @@ var buildFullPageClip = (metrics, viewport, maxClipHeight) => {
|
|
|
532
542
|
scale: 1
|
|
533
543
|
};
|
|
534
544
|
};
|
|
545
|
+
var expandScrollableContent = async (page, options = {}) => {
|
|
546
|
+
return await page.evaluate(({ className, forceScrollableHeight, visibleOnly }) => {
|
|
547
|
+
const body = document.body;
|
|
548
|
+
const root = document.documentElement;
|
|
549
|
+
const scrollingElement = document.scrollingElement;
|
|
550
|
+
const candidates = [];
|
|
551
|
+
const seen = /* @__PURE__ */ new Set();
|
|
552
|
+
let maxHeight = window.innerHeight || 0;
|
|
553
|
+
const pushCandidate = (el) => {
|
|
554
|
+
if (!el || seen.has(el)) return;
|
|
555
|
+
seen.add(el);
|
|
556
|
+
candidates.push(el);
|
|
557
|
+
};
|
|
558
|
+
pushCandidate(scrollingElement);
|
|
559
|
+
pushCandidate(root);
|
|
560
|
+
pushCandidate(body);
|
|
561
|
+
document.querySelectorAll("*").forEach(pushCandidate);
|
|
562
|
+
const isDocumentElement = (el) => el === root || el === body || el === scrollingElement;
|
|
563
|
+
const isVisible = (el, style, rect) => {
|
|
564
|
+
if (!el || !style || !rect) return false;
|
|
565
|
+
if (style.display === "none" || style.visibility === "hidden" || style.visibility === "collapse") {
|
|
566
|
+
return false;
|
|
567
|
+
}
|
|
568
|
+
if (Number(style.opacity) === 0) return false;
|
|
569
|
+
return rect.width > 0 && rect.height > 0;
|
|
570
|
+
};
|
|
571
|
+
const isScrollableY = (el, style) => {
|
|
572
|
+
if (!el || el.scrollHeight <= el.clientHeight + 1) {
|
|
573
|
+
return false;
|
|
574
|
+
}
|
|
575
|
+
if (isDocumentElement(el)) {
|
|
576
|
+
return true;
|
|
577
|
+
}
|
|
578
|
+
const overflowY = String(style.overflowY || "").toLowerCase();
|
|
579
|
+
const overflow = String(style.overflow || "").toLowerCase();
|
|
580
|
+
const scrollableOverflow = /* @__PURE__ */ new Set(["auto", "scroll", "overlay"]);
|
|
581
|
+
return scrollableOverflow.has(overflowY) || scrollableOverflow.has(overflow);
|
|
582
|
+
};
|
|
583
|
+
candidates.forEach((el) => {
|
|
584
|
+
const style = window.getComputedStyle(el);
|
|
585
|
+
const rect = el.getBoundingClientRect();
|
|
586
|
+
if (visibleOnly && !isVisible(el, style, rect)) {
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
if (!isScrollableY(el, style)) {
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
const scrollHeight = Math.ceil(el.scrollHeight || 0);
|
|
593
|
+
if (scrollHeight <= 0) {
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
if (scrollHeight > maxHeight) {
|
|
597
|
+
maxHeight = scrollHeight;
|
|
598
|
+
}
|
|
599
|
+
if (isDocumentElement(el)) {
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
if (!el.classList.contains(className)) {
|
|
603
|
+
el.dataset.pkOrigOverflow = el.style.overflow;
|
|
604
|
+
el.dataset.pkOrigOverflowPriority = el.style.getPropertyPriority("overflow") || "";
|
|
605
|
+
el.dataset.pkOrigHeight = el.style.height;
|
|
606
|
+
el.dataset.pkOrigHeightPriority = el.style.getPropertyPriority("height") || "";
|
|
607
|
+
el.dataset.pkOrigMinHeight = el.style.minHeight;
|
|
608
|
+
el.dataset.pkOrigMinHeightPriority = el.style.getPropertyPriority("min-height") || "";
|
|
609
|
+
el.dataset.pkOrigMaxHeight = el.style.maxHeight;
|
|
610
|
+
el.dataset.pkOrigMaxHeightPriority = el.style.getPropertyPriority("max-height") || "";
|
|
611
|
+
el.classList.add(className);
|
|
612
|
+
}
|
|
613
|
+
if (forceScrollableHeight) {
|
|
614
|
+
el.style.setProperty("overflow", "visible", "important");
|
|
615
|
+
el.style.setProperty("height", `${scrollHeight}px`, "important");
|
|
616
|
+
el.style.setProperty("min-height", `${scrollHeight}px`, "important");
|
|
617
|
+
el.style.setProperty("max-height", "none", "important");
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
el.style.overflow = "visible";
|
|
621
|
+
el.style.height = "auto";
|
|
622
|
+
el.style.maxHeight = "none";
|
|
623
|
+
});
|
|
624
|
+
return maxHeight;
|
|
625
|
+
}, {
|
|
626
|
+
className: EXPANDED_SCROLLABLE_CLASS,
|
|
627
|
+
forceScrollableHeight: Boolean(options.forceScrollableHeight),
|
|
628
|
+
visibleOnly: options.visibleOnly !== false
|
|
629
|
+
});
|
|
630
|
+
};
|
|
631
|
+
var prepareExpandedFullPageScreenshot = async (page, options = {}) => {
|
|
632
|
+
const originalViewport = await resolveCurrentViewportSize(page);
|
|
633
|
+
const defaultBuffer = Math.round((originalViewport.height || 1080) / 2);
|
|
634
|
+
const buffer = options.buffer ?? defaultBuffer;
|
|
635
|
+
const maxHeight = toPositiveInteger(options.maxHeight, DEFAULT_MAX_HEIGHT);
|
|
636
|
+
const settleMs = Math.max(0, Number(options.settleMs ?? DEFAULT_SETTLE_MS) || 0);
|
|
637
|
+
const maxScrollHeight = await expandScrollableContent(page, {
|
|
638
|
+
forceScrollableHeight: options.forceScrollableHeight,
|
|
639
|
+
visibleOnly: options.visibleOnly !== false
|
|
640
|
+
});
|
|
641
|
+
const targetHeight = Math.min(maxScrollHeight + buffer, maxHeight);
|
|
642
|
+
await page.setViewportSize({
|
|
643
|
+
width: originalViewport.width,
|
|
644
|
+
height: targetHeight
|
|
645
|
+
});
|
|
646
|
+
if (settleMs > 0) {
|
|
647
|
+
await (0, import_delay.default)(settleMs);
|
|
648
|
+
}
|
|
649
|
+
return {
|
|
650
|
+
originalViewport,
|
|
651
|
+
maxScrollHeight,
|
|
652
|
+
targetHeight
|
|
653
|
+
};
|
|
654
|
+
};
|
|
655
|
+
var restoreExpandedFullPageScreenshot = async (page, state = {}) => {
|
|
656
|
+
await page.evaluate((className) => {
|
|
657
|
+
document.querySelectorAll(`.${className}`).forEach((el) => {
|
|
658
|
+
el.style.setProperty("overflow", el.dataset.pkOrigOverflow || "", el.dataset.pkOrigOverflowPriority || "");
|
|
659
|
+
el.style.setProperty("height", el.dataset.pkOrigHeight || "", el.dataset.pkOrigHeightPriority || "");
|
|
660
|
+
el.style.setProperty("min-height", el.dataset.pkOrigMinHeight || "", el.dataset.pkOrigMinHeightPriority || "");
|
|
661
|
+
el.style.setProperty("max-height", el.dataset.pkOrigMaxHeight || "", el.dataset.pkOrigMaxHeightPriority || "");
|
|
662
|
+
delete el.dataset.pkOrigOverflow;
|
|
663
|
+
delete el.dataset.pkOrigOverflowPriority;
|
|
664
|
+
delete el.dataset.pkOrigHeight;
|
|
665
|
+
delete el.dataset.pkOrigHeightPriority;
|
|
666
|
+
delete el.dataset.pkOrigMinHeight;
|
|
667
|
+
delete el.dataset.pkOrigMinHeightPriority;
|
|
668
|
+
delete el.dataset.pkOrigMaxHeight;
|
|
669
|
+
delete el.dataset.pkOrigMaxHeightPriority;
|
|
670
|
+
el.classList.remove(className);
|
|
671
|
+
});
|
|
672
|
+
}, EXPANDED_SCROLLABLE_CLASS);
|
|
673
|
+
if (state?.originalViewport) {
|
|
674
|
+
await page.setViewportSize(state.originalViewport);
|
|
675
|
+
}
|
|
676
|
+
};
|
|
535
677
|
var capturePageScreenshot = async (page, options = {}) => {
|
|
536
678
|
const fullPage = Boolean(options.fullPage);
|
|
537
679
|
const type = fullPage ? FORCED_FULLPAGE_TYPE : normalizeType(options.type);
|
|
@@ -585,6 +727,22 @@ var capturePageScreenshot = async (page, options = {}) => {
|
|
|
585
727
|
return await page.screenshot(fallbackOptions);
|
|
586
728
|
}
|
|
587
729
|
};
|
|
730
|
+
var captureExpandedFullPageScreenshot = async (page, options = {}) => {
|
|
731
|
+
const state = await prepareExpandedFullPageScreenshot(page, options);
|
|
732
|
+
try {
|
|
733
|
+
return await capturePageScreenshot(page, {
|
|
734
|
+
fullPage: true,
|
|
735
|
+
type: options.type || "png",
|
|
736
|
+
quality: options.quality,
|
|
737
|
+
timeout: options.timeout,
|
|
738
|
+
maxClipHeight: state.targetHeight
|
|
739
|
+
});
|
|
740
|
+
} finally {
|
|
741
|
+
if (options.restore) {
|
|
742
|
+
await restoreExpandedFullPageScreenshot(page, state);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
};
|
|
588
746
|
|
|
589
747
|
// src/errors.js
|
|
590
748
|
var errors_exports = {};
|
|
@@ -2124,7 +2282,7 @@ var AntiCheat = {
|
|
|
2124
2282
|
};
|
|
2125
2283
|
|
|
2126
2284
|
// src/internals/humanize/desktop.js
|
|
2127
|
-
var
|
|
2285
|
+
var import_delay2 = __toESM(require("delay"), 1);
|
|
2128
2286
|
var import_ghost_cursor_playwright = require("ghost-cursor-playwright");
|
|
2129
2287
|
var logger6 = createInternalLogger("Humanize");
|
|
2130
2288
|
var $CursorWeakMap = /* @__PURE__ */ new WeakMap();
|
|
@@ -2356,7 +2514,7 @@ var Humanize = {
|
|
|
2356
2514
|
}
|
|
2357
2515
|
await page.mouse.wheel(0, deltaY);
|
|
2358
2516
|
didScroll = true;
|
|
2359
|
-
await (0,
|
|
2517
|
+
await (0, import_delay2.default)(this.jitterMs(20 + Math.random() * 40, 0.2));
|
|
2360
2518
|
}
|
|
2361
2519
|
logger6.warn(`humanScroll | \u5728 ${maxSteps} \u6B65\u540E\u65E0\u6CD5\u786E\u4FDD\u53EF\u89C1\u6027`);
|
|
2362
2520
|
return { element, didScroll };
|
|
@@ -2385,7 +2543,7 @@ var Humanize = {
|
|
|
2385
2543
|
restoreOnce.restored = true;
|
|
2386
2544
|
if (typeof restoreOnce.do !== "function") return;
|
|
2387
2545
|
try {
|
|
2388
|
-
await (0,
|
|
2546
|
+
await (0, import_delay2.default)(this.jitterMs(1e3));
|
|
2389
2547
|
await restoreOnce.do();
|
|
2390
2548
|
} catch (restoreError) {
|
|
2391
2549
|
logger6.warn(`humanClick: \u6062\u590D\u6EDA\u52A8\u4F4D\u7F6E\u5931\u8D25: ${restoreError.message}`);
|
|
@@ -2393,7 +2551,7 @@ var Humanize = {
|
|
|
2393
2551
|
};
|
|
2394
2552
|
try {
|
|
2395
2553
|
if (target == null) {
|
|
2396
|
-
await (0,
|
|
2554
|
+
await (0, import_delay2.default)(this.jitterMs(reactionDelay, 0.4));
|
|
2397
2555
|
await cursor.actions.click();
|
|
2398
2556
|
logger6.success("humanClick", "Clicked current position");
|
|
2399
2557
|
return true;
|
|
@@ -2427,7 +2585,7 @@ var Humanize = {
|
|
|
2427
2585
|
const x = box.x + box.width / 2 + (Math.random() - 0.5) * box.width * 0.3;
|
|
2428
2586
|
const y = box.y + box.height / 2 + (Math.random() - 0.5) * box.height * 0.3;
|
|
2429
2587
|
await cursor.actions.move({ x, y });
|
|
2430
|
-
await (0,
|
|
2588
|
+
await (0, import_delay2.default)(this.jitterMs(reactionDelay, 0.4));
|
|
2431
2589
|
await cursor.actions.click();
|
|
2432
2590
|
await restoreOnce();
|
|
2433
2591
|
logger6.success("humanClick");
|
|
@@ -2446,7 +2604,7 @@ var Humanize = {
|
|
|
2446
2604
|
async randomSleep(baseMs, jitterPercent = 0.3) {
|
|
2447
2605
|
const ms = this.jitterMs(baseMs, jitterPercent);
|
|
2448
2606
|
logger6.start("randomSleep", `base=${baseMs}, actual=${ms}ms`);
|
|
2449
|
-
await (0,
|
|
2607
|
+
await (0, import_delay2.default)(ms);
|
|
2450
2608
|
logger6.success("randomSleep");
|
|
2451
2609
|
},
|
|
2452
2610
|
/**
|
|
@@ -2464,7 +2622,7 @@ var Humanize = {
|
|
|
2464
2622
|
const x = 100 + Math.random() * (viewportSize.width - 200);
|
|
2465
2623
|
const y = 100 + Math.random() * (viewportSize.height - 200);
|
|
2466
2624
|
await cursor.actions.move({ x, y });
|
|
2467
|
-
await (0,
|
|
2625
|
+
await (0, import_delay2.default)(this.jitterMs(600, 0.5));
|
|
2468
2626
|
}
|
|
2469
2627
|
logger6.success("simulateGaze");
|
|
2470
2628
|
},
|
|
@@ -2488,7 +2646,7 @@ var Humanize = {
|
|
|
2488
2646
|
try {
|
|
2489
2647
|
const locator = page.locator(selector);
|
|
2490
2648
|
await Humanize.humanClick(page, locator);
|
|
2491
|
-
await (0,
|
|
2649
|
+
await (0, import_delay2.default)(this.jitterMs(200, 0.4));
|
|
2492
2650
|
for (let i = 0; i < text.length; i++) {
|
|
2493
2651
|
const char = text[i];
|
|
2494
2652
|
let charDelay;
|
|
@@ -2500,11 +2658,11 @@ var Humanize = {
|
|
|
2500
2658
|
charDelay = this.jitterMs(baseDelay, 0.4);
|
|
2501
2659
|
}
|
|
2502
2660
|
await page.keyboard.type(char);
|
|
2503
|
-
await (0,
|
|
2661
|
+
await (0, import_delay2.default)(charDelay);
|
|
2504
2662
|
if (Math.random() < pauseProbability && i < text.length - 1) {
|
|
2505
2663
|
const pauseTime = this.jitterMs(pauseBase, 0.5);
|
|
2506
2664
|
logger6.debug(`\u505C\u987F ${pauseTime}ms...`);
|
|
2507
|
-
await (0,
|
|
2665
|
+
await (0, import_delay2.default)(pauseTime);
|
|
2508
2666
|
}
|
|
2509
2667
|
}
|
|
2510
2668
|
logger6.success("humanType");
|
|
@@ -2513,6 +2671,43 @@ var Humanize = {
|
|
|
2513
2671
|
throw error;
|
|
2514
2672
|
}
|
|
2515
2673
|
},
|
|
2674
|
+
/**
|
|
2675
|
+
* 人类化按键 - 模拟用户在当前焦点或指定目标上按下一次键。
|
|
2676
|
+
* @param {import('playwright').Page} page
|
|
2677
|
+
* @param {string|import('playwright').ElementHandle|import('playwright').Locator} targetOrKey - 目标或按键
|
|
2678
|
+
* @param {string|Object} [maybeKey] - 按键或选项
|
|
2679
|
+
* @param {Object} [options]
|
|
2680
|
+
*/
|
|
2681
|
+
async humanPress(page, targetOrKey, maybeKey, options = {}) {
|
|
2682
|
+
const hasTarget = typeof maybeKey === "string";
|
|
2683
|
+
const key = hasTarget ? maybeKey : targetOrKey;
|
|
2684
|
+
const pressOptions = hasTarget ? options : maybeKey || options;
|
|
2685
|
+
const {
|
|
2686
|
+
reactionDelay = 180,
|
|
2687
|
+
holdDelay = 45,
|
|
2688
|
+
focusDelay = 180,
|
|
2689
|
+
scrollIfNeeded = true,
|
|
2690
|
+
throwOnMissing = true,
|
|
2691
|
+
keyboardOptions = {}
|
|
2692
|
+
} = pressOptions || {};
|
|
2693
|
+
const targetDesc = hasTarget ? typeof targetOrKey === "string" ? targetOrKey : "ElementHandle" : "current focus";
|
|
2694
|
+
logger6.start("humanPress", `key=${key}, target=${targetDesc}`);
|
|
2695
|
+
try {
|
|
2696
|
+
if (hasTarget) {
|
|
2697
|
+
await this.humanClick(page, targetOrKey, { reactionDelay: focusDelay, scrollIfNeeded, throwOnMissing });
|
|
2698
|
+
}
|
|
2699
|
+
await (0, import_delay2.default)(this.jitterMs(reactionDelay, 0.45));
|
|
2700
|
+
await page.keyboard.press(key, {
|
|
2701
|
+
...keyboardOptions,
|
|
2702
|
+
delay: this.jitterMs(holdDelay, 0.5)
|
|
2703
|
+
});
|
|
2704
|
+
logger6.success("humanPress");
|
|
2705
|
+
return true;
|
|
2706
|
+
} catch (error) {
|
|
2707
|
+
logger6.fail("humanPress", error);
|
|
2708
|
+
throw error;
|
|
2709
|
+
}
|
|
2710
|
+
},
|
|
2516
2711
|
/**
|
|
2517
2712
|
* 人类化清空输入框 - 模拟人类删除文本的行为
|
|
2518
2713
|
* @param {import('playwright').Page} page
|
|
@@ -2523,14 +2718,14 @@ var Humanize = {
|
|
|
2523
2718
|
try {
|
|
2524
2719
|
const locator = page.locator(selector);
|
|
2525
2720
|
await locator.click();
|
|
2526
|
-
await (0,
|
|
2721
|
+
await (0, import_delay2.default)(this.jitterMs(200, 0.4));
|
|
2527
2722
|
const currentValue = await locator.inputValue();
|
|
2528
2723
|
if (!currentValue || currentValue.length === 0) {
|
|
2529
2724
|
logger6.success("humanClear", "already empty");
|
|
2530
2725
|
return;
|
|
2531
2726
|
}
|
|
2532
2727
|
await page.keyboard.press("Meta+A");
|
|
2533
|
-
await (0,
|
|
2728
|
+
await (0, import_delay2.default)(this.jitterMs(100, 0.4));
|
|
2534
2729
|
await page.keyboard.press("Backspace");
|
|
2535
2730
|
logger6.success("humanClear");
|
|
2536
2731
|
} catch (error) {
|
|
@@ -2556,13 +2751,13 @@ var Humanize = {
|
|
|
2556
2751
|
const x = 100 + Math.random() * (viewportSize.width - 200);
|
|
2557
2752
|
const y = 100 + Math.random() * (viewportSize.height - 200);
|
|
2558
2753
|
await cursor.actions.move({ x, y });
|
|
2559
|
-
await (0,
|
|
2754
|
+
await (0, import_delay2.default)(this.jitterMs(350, 0.4));
|
|
2560
2755
|
} else if (action < 0.7) {
|
|
2561
2756
|
const scrollY = (Math.random() - 0.5) * 200;
|
|
2562
2757
|
await page.mouse.wheel(0, scrollY);
|
|
2563
|
-
await (0,
|
|
2758
|
+
await (0, import_delay2.default)(this.jitterMs(500, 0.4));
|
|
2564
2759
|
} else {
|
|
2565
|
-
await (0,
|
|
2760
|
+
await (0, import_delay2.default)(this.jitterMs(800, 0.5));
|
|
2566
2761
|
}
|
|
2567
2762
|
}
|
|
2568
2763
|
logger6.success("warmUpBrowsing");
|
|
@@ -2591,7 +2786,7 @@ var Humanize = {
|
|
|
2591
2786
|
const scrollAmount = stepDistance * factor * sign * jitter;
|
|
2592
2787
|
await page.mouse.wheel(0, scrollAmount);
|
|
2593
2788
|
const baseDelay = 60 + i * 25;
|
|
2594
|
-
await (0,
|
|
2789
|
+
await (0, import_delay2.default)(this.jitterMs(baseDelay, 0.3));
|
|
2595
2790
|
}
|
|
2596
2791
|
logger6.success("naturalScroll");
|
|
2597
2792
|
} catch (error) {
|
|
@@ -2601,8 +2796,143 @@ var Humanize = {
|
|
|
2601
2796
|
}
|
|
2602
2797
|
};
|
|
2603
2798
|
|
|
2799
|
+
// src/internals/humanize/machine.js
|
|
2800
|
+
var resolveDeviceFromPage = (page) => normalizeDevice(page?.[PageRuntimeStateKey]?.device);
|
|
2801
|
+
var isPoint = (value) => value && typeof value === "object" && Number.isFinite(Number(value.x)) && Number.isFinite(Number(value.y));
|
|
2802
|
+
var resolveNativeTarget = (page, target) => {
|
|
2803
|
+
if (!target) return null;
|
|
2804
|
+
if (typeof target === "string") {
|
|
2805
|
+
return page.locator(target);
|
|
2806
|
+
}
|
|
2807
|
+
return target;
|
|
2808
|
+
};
|
|
2809
|
+
var normalizeSelectorOptions = (options = {}) => ({
|
|
2810
|
+
targetParent: Boolean(options.targetParent ?? options.parent ?? false),
|
|
2811
|
+
pick: options.pick === "last" || options.last ? "last" : "first",
|
|
2812
|
+
visibleOnly: options.visibleOnly !== false,
|
|
2813
|
+
fallbackDomClick: Boolean(options.fallbackDomClick),
|
|
2814
|
+
forceMouse: Boolean(options.forceMouse)
|
|
2815
|
+
});
|
|
2816
|
+
var MachineHumanize = {
|
|
2817
|
+
async clickPoint(page, pointOrX, maybeY, options = {}) {
|
|
2818
|
+
const clickOptions = isPoint(pointOrX) && maybeY && typeof maybeY === "object" ? maybeY : options;
|
|
2819
|
+
const point = isPoint(pointOrX) ? pointOrX : { x: pointOrX, y: maybeY };
|
|
2820
|
+
const x = Number(point.x);
|
|
2821
|
+
const y = Number(point.y);
|
|
2822
|
+
if (!Number.isFinite(x) || !Number.isFinite(y)) {
|
|
2823
|
+
throw new Error(`Invalid click point: ${JSON.stringify(point)}`);
|
|
2824
|
+
}
|
|
2825
|
+
const resolvedDevice = resolveDeviceFromPage(page);
|
|
2826
|
+
if (resolvedDevice === Device.Mobile && !clickOptions.forceMouse && page.touchscreen && typeof page.touchscreen.tap === "function") {
|
|
2827
|
+
await page.touchscreen.tap(x, y);
|
|
2828
|
+
return true;
|
|
2829
|
+
}
|
|
2830
|
+
await page.mouse.click(x, y, clickOptions.mouseOptions || {});
|
|
2831
|
+
return true;
|
|
2832
|
+
},
|
|
2833
|
+
async click(page, target, options = {}) {
|
|
2834
|
+
if (isPoint(target)) {
|
|
2835
|
+
return MachineHumanize.clickPoint(page, target, void 0, options);
|
|
2836
|
+
}
|
|
2837
|
+
const nativeTarget = resolveNativeTarget(page, target);
|
|
2838
|
+
if (!nativeTarget || typeof nativeTarget.click !== "function") {
|
|
2839
|
+
throw new Error("Machine click target does not expose click()");
|
|
2840
|
+
}
|
|
2841
|
+
await nativeTarget.click(options.clickOptions || options);
|
|
2842
|
+
return true;
|
|
2843
|
+
},
|
|
2844
|
+
async focus(page, target, options = {}) {
|
|
2845
|
+
const nativeTarget = resolveNativeTarget(page, target);
|
|
2846
|
+
if (!nativeTarget || typeof nativeTarget.focus !== "function") {
|
|
2847
|
+
throw new Error("Machine focus target does not expose focus()");
|
|
2848
|
+
}
|
|
2849
|
+
await nativeTarget.focus(options);
|
|
2850
|
+
return true;
|
|
2851
|
+
},
|
|
2852
|
+
async fill(page, target, value, options = {}) {
|
|
2853
|
+
const nativeTarget = resolveNativeTarget(page, target);
|
|
2854
|
+
if (!nativeTarget || typeof nativeTarget.fill !== "function") {
|
|
2855
|
+
throw new Error("Machine fill target does not expose fill()");
|
|
2856
|
+
}
|
|
2857
|
+
await nativeTarget.fill(value, options);
|
|
2858
|
+
return true;
|
|
2859
|
+
},
|
|
2860
|
+
async keyboardType(page, text, options = {}) {
|
|
2861
|
+
await page.keyboard.type(text, options);
|
|
2862
|
+
return true;
|
|
2863
|
+
},
|
|
2864
|
+
async type(page, targetOrText, maybeText, options = {}) {
|
|
2865
|
+
if (maybeText == null || typeof maybeText === "object" && !Array.isArray(maybeText)) {
|
|
2866
|
+
return MachineHumanize.keyboardType(page, targetOrText, maybeText || options);
|
|
2867
|
+
}
|
|
2868
|
+
const nativeTarget = resolveNativeTarget(page, targetOrText);
|
|
2869
|
+
if (!nativeTarget || typeof nativeTarget.type !== "function") {
|
|
2870
|
+
throw new Error("Machine type target does not expose type()");
|
|
2871
|
+
}
|
|
2872
|
+
await nativeTarget.type(maybeText, options);
|
|
2873
|
+
return true;
|
|
2874
|
+
},
|
|
2875
|
+
async selectorCenterPoint(page, selector, options = {}) {
|
|
2876
|
+
const normalizedOptions = normalizeSelectorOptions(options);
|
|
2877
|
+
return page.evaluate(({ innerSelector, innerOptions }) => {
|
|
2878
|
+
const nodes = Array.from(document.querySelectorAll(innerSelector));
|
|
2879
|
+
const ordered = innerOptions.pick === "last" ? nodes.reverse() : nodes;
|
|
2880
|
+
let rect = null;
|
|
2881
|
+
for (const node of ordered) {
|
|
2882
|
+
if (!node) continue;
|
|
2883
|
+
const target = innerOptions.targetParent ? node.parentElement || node : node;
|
|
2884
|
+
rect = target.getBoundingClientRect?.() || null;
|
|
2885
|
+
const isVisible = Boolean(rect && rect.width > 0 && rect.height > 0);
|
|
2886
|
+
if (innerOptions.visibleOnly && !isVisible) continue;
|
|
2887
|
+
break;
|
|
2888
|
+
}
|
|
2889
|
+
if (!rect || rect.width <= 0 || rect.height <= 0) return null;
|
|
2890
|
+
return {
|
|
2891
|
+
x: rect.left + rect.width / 2,
|
|
2892
|
+
y: rect.top + rect.height / 2
|
|
2893
|
+
};
|
|
2894
|
+
}, { innerSelector: selector, innerOptions: normalizedOptions });
|
|
2895
|
+
},
|
|
2896
|
+
async domClick(page, selector, options = {}) {
|
|
2897
|
+
const normalizedOptions = normalizeSelectorOptions(options);
|
|
2898
|
+
return page.evaluate(({ innerSelector, innerOptions }) => {
|
|
2899
|
+
const nodes = Array.from(document.querySelectorAll(innerSelector));
|
|
2900
|
+
const ordered = innerOptions.pick === "last" ? nodes.reverse() : nodes;
|
|
2901
|
+
let target = null;
|
|
2902
|
+
for (const node of ordered) {
|
|
2903
|
+
if (!node) continue;
|
|
2904
|
+
const candidate = innerOptions.targetParent ? node.parentElement || node : node;
|
|
2905
|
+
const rect = candidate.getBoundingClientRect?.() || null;
|
|
2906
|
+
const isVisible = Boolean(rect && rect.width > 0 && rect.height > 0);
|
|
2907
|
+
if (innerOptions.visibleOnly && !isVisible) continue;
|
|
2908
|
+
target = candidate;
|
|
2909
|
+
break;
|
|
2910
|
+
}
|
|
2911
|
+
if (!target || typeof target.click !== "function") return false;
|
|
2912
|
+
target.click();
|
|
2913
|
+
return true;
|
|
2914
|
+
}, { innerSelector: selector, innerOptions: normalizedOptions });
|
|
2915
|
+
},
|
|
2916
|
+
async clickSelectorCenter(page, selector, options = {}) {
|
|
2917
|
+
const normalizedOptions = normalizeSelectorOptions(options);
|
|
2918
|
+
try {
|
|
2919
|
+
const point = await MachineHumanize.selectorCenterPoint(page, selector, normalizedOptions);
|
|
2920
|
+
if (point) {
|
|
2921
|
+
await MachineHumanize.clickPoint(page, point, void 0, normalizedOptions);
|
|
2922
|
+
return true;
|
|
2923
|
+
}
|
|
2924
|
+
} catch (error) {
|
|
2925
|
+
if (!normalizedOptions.fallbackDomClick) throw error;
|
|
2926
|
+
}
|
|
2927
|
+
if (normalizedOptions.fallbackDomClick) {
|
|
2928
|
+
return MachineHumanize.domClick(page, selector, normalizedOptions);
|
|
2929
|
+
}
|
|
2930
|
+
return false;
|
|
2931
|
+
}
|
|
2932
|
+
};
|
|
2933
|
+
|
|
2604
2934
|
// src/internals/humanize/shared.js
|
|
2605
|
-
var
|
|
2935
|
+
var import_delay3 = __toESM(require("delay"), 1);
|
|
2606
2936
|
var jitterMs = (base, jitterPercent = 0.3) => {
|
|
2607
2937
|
const jitter = Number(base || 0) * Number(jitterPercent || 0) * (Math.random() * 2 - 1);
|
|
2608
2938
|
return Math.max(10, Math.round(Number(base || 0) + jitter));
|
|
@@ -2628,7 +2958,7 @@ var resolveElement = async (page, target, { throwOnMissing = true } = {}) => {
|
|
|
2628
2958
|
}
|
|
2629
2959
|
return element;
|
|
2630
2960
|
};
|
|
2631
|
-
var waitJitter = (base, jitterPercent = 0.3) => (0,
|
|
2961
|
+
var waitJitter = (base, jitterPercent = 0.3) => (0, import_delay3.default)(jitterMs(base, jitterPercent));
|
|
2632
2962
|
|
|
2633
2963
|
// src/internals/humanize/mobile.js
|
|
2634
2964
|
var logger7 = createInternalLogger("Humanize.Mobile");
|
|
@@ -2930,6 +3260,49 @@ var scrollScrollableAncestor = async (element, deltaY) => {
|
|
|
2930
3260
|
};
|
|
2931
3261
|
}, deltaY);
|
|
2932
3262
|
};
|
|
3263
|
+
var scrollAwayFromObstruction = async (element, status) => {
|
|
3264
|
+
return element.evaluate((el, innerStatus) => {
|
|
3265
|
+
const isScrollable = (node) => {
|
|
3266
|
+
const style = window.getComputedStyle(node);
|
|
3267
|
+
if (!style) return false;
|
|
3268
|
+
const overflowY = style.overflowY;
|
|
3269
|
+
if (!["auto", "scroll", "overlay"].includes(overflowY)) return false;
|
|
3270
|
+
return node.scrollHeight > node.clientHeight + 1;
|
|
3271
|
+
};
|
|
3272
|
+
let scroller = el;
|
|
3273
|
+
while (scroller && scroller !== document.body) {
|
|
3274
|
+
if (isScrollable(scroller)) break;
|
|
3275
|
+
scroller = scroller.parentElement;
|
|
3276
|
+
}
|
|
3277
|
+
if (!scroller || scroller === document.body) {
|
|
3278
|
+
return { moved: false, scrollTop: 0, deltaY: 0 };
|
|
3279
|
+
}
|
|
3280
|
+
const rect = el.getBoundingClientRect();
|
|
3281
|
+
const scrollerRect = scroller.getBoundingClientRect();
|
|
3282
|
+
const obstruction = innerStatus?.obstruction || {};
|
|
3283
|
+
const obstructionTop = Number(obstruction.top);
|
|
3284
|
+
const obstructionBottom = Number(obstruction.bottom);
|
|
3285
|
+
const obstructionMiddle = Number.isFinite(obstructionTop) && Number.isFinite(obstructionBottom) ? (obstructionTop + obstructionBottom) / 2 : scrollerRect.top + scrollerRect.height / 2;
|
|
3286
|
+
const scrollerMiddle = scrollerRect.top + scrollerRect.height / 2;
|
|
3287
|
+
const padding = 18;
|
|
3288
|
+
let deltaY = 0;
|
|
3289
|
+
if (Number.isFinite(obstructionTop) && rect.bottom > obstructionTop && obstructionTop >= scrollerRect.top && obstructionTop <= scrollerRect.bottom && obstructionMiddle >= scrollerMiddle) {
|
|
3290
|
+
deltaY = rect.bottom - obstructionTop + padding;
|
|
3291
|
+
} else if (Number.isFinite(obstructionBottom) && rect.top < obstructionBottom && obstructionBottom >= scrollerRect.top && obstructionBottom <= scrollerRect.bottom && obstructionMiddle < scrollerMiddle) {
|
|
3292
|
+
deltaY = rect.top - obstructionBottom - padding;
|
|
3293
|
+
} else {
|
|
3294
|
+
const fallbackDistance = Math.max(48, Math.min(180, scrollerRect.height * 0.45));
|
|
3295
|
+
deltaY = obstructionMiddle >= scrollerMiddle ? fallbackDistance : -fallbackDistance;
|
|
3296
|
+
}
|
|
3297
|
+
const beforeTop = scroller.scrollTop;
|
|
3298
|
+
scroller.scrollTop = beforeTop + deltaY;
|
|
3299
|
+
return {
|
|
3300
|
+
moved: Math.abs(scroller.scrollTop - beforeTop) > 2,
|
|
3301
|
+
scrollTop: scroller.scrollTop,
|
|
3302
|
+
deltaY
|
|
3303
|
+
};
|
|
3304
|
+
}, status);
|
|
3305
|
+
};
|
|
2933
3306
|
var dispatchTouchSwipe = async (page, deltaY, options = {}) => {
|
|
2934
3307
|
const viewport = resolveViewport(page);
|
|
2935
3308
|
const rawRect = options.rect || null;
|
|
@@ -3093,6 +3466,15 @@ var MobileHumanize = {
|
|
|
3093
3466
|
logger7.warn(`humanScroll | fixed/sticky \u76EE\u6807\u88AB\u906E\u6321\uFF0C\u6EDA\u52A8\u65E0\u6CD5\u89E3\u9664 (${status.obstruction?.tag || "unknown"})`);
|
|
3094
3467
|
return { element, didScroll, restore: null };
|
|
3095
3468
|
}
|
|
3469
|
+
if (scrollRect && status.code === "OBSTRUCTED" && status.obstruction?.isFixed) {
|
|
3470
|
+
const moved = await scrollAwayFromObstruction(element, status);
|
|
3471
|
+
if (moved.moved) {
|
|
3472
|
+
logger7.debug(`humanScroll | sticky/fixed \u906E\u6321\u8865\u507F\u6EDA\u52A8 top=${Math.round(moved.scrollTop || 0)}`);
|
|
3473
|
+
await waitJitter(90, 0.3);
|
|
3474
|
+
didScroll = true;
|
|
3475
|
+
continue;
|
|
3476
|
+
}
|
|
3477
|
+
}
|
|
3096
3478
|
if (Date.now() - startTime > maxDurationMs) {
|
|
3097
3479
|
logger7.warn(`humanScroll | mobile timeout (${maxDurationMs}ms, status=${status.code}, direction=${status.direction || "unknown"}, fixed=${Boolean(status.isFixed)})`);
|
|
3098
3480
|
return { element, didScroll, restore: null };
|
|
@@ -3224,7 +3606,7 @@ var MobileHumanize = {
|
|
|
3224
3606
|
await MobileHumanize.humanScroll(page, element, { throwOnMissing });
|
|
3225
3607
|
}
|
|
3226
3608
|
const status = await checkElementVisibility(element).catch(() => null);
|
|
3227
|
-
if (status &&
|
|
3609
|
+
if (status && status.code !== "VISIBLE") {
|
|
3228
3610
|
const message = `\u5143\u7D20\u4E0D\u53EF\u70B9\u51FB: ${status.reason || status.code}`;
|
|
3229
3611
|
if (throwOnMissing) throw new Error(message);
|
|
3230
3612
|
logger7.warn(`humanClick: ${message}\uFF0C\u8DF3\u8FC7\u70B9\u51FB`);
|
|
@@ -3284,6 +3666,40 @@ var MobileHumanize = {
|
|
|
3284
3666
|
}
|
|
3285
3667
|
}
|
|
3286
3668
|
},
|
|
3669
|
+
async humanPress(page, targetOrKey, maybeKey, options = {}) {
|
|
3670
|
+
const hasTarget = typeof maybeKey === "string";
|
|
3671
|
+
const key = hasTarget ? maybeKey : targetOrKey;
|
|
3672
|
+
const pressOptions = hasTarget ? options : maybeKey || options;
|
|
3673
|
+
const {
|
|
3674
|
+
reactionDelay = 170,
|
|
3675
|
+
holdDelay = 42,
|
|
3676
|
+
focusDelay = 180,
|
|
3677
|
+
scrollIfNeeded = true,
|
|
3678
|
+
throwOnMissing = true,
|
|
3679
|
+
keyboardOptions = {}
|
|
3680
|
+
} = pressOptions || {};
|
|
3681
|
+
const targetDesc = hasTarget ? describeTarget(targetOrKey) : "current focus";
|
|
3682
|
+
logger7.start("humanPress", `key=${key}, target=${targetDesc}`);
|
|
3683
|
+
try {
|
|
3684
|
+
if (hasTarget) {
|
|
3685
|
+
await MobileHumanize.humanClick(page, targetOrKey, {
|
|
3686
|
+
reactionDelay: focusDelay,
|
|
3687
|
+
scrollIfNeeded,
|
|
3688
|
+
throwOnMissing
|
|
3689
|
+
});
|
|
3690
|
+
}
|
|
3691
|
+
await waitJitter(reactionDelay, 0.45);
|
|
3692
|
+
await page.keyboard.press(key, {
|
|
3693
|
+
...keyboardOptions,
|
|
3694
|
+
delay: jitterMs(holdDelay, 0.5)
|
|
3695
|
+
});
|
|
3696
|
+
logger7.success("humanPress");
|
|
3697
|
+
return true;
|
|
3698
|
+
} catch (error) {
|
|
3699
|
+
logger7.fail("humanPress", error);
|
|
3700
|
+
throw error;
|
|
3701
|
+
}
|
|
3702
|
+
},
|
|
3287
3703
|
async humanClear(page, selector) {
|
|
3288
3704
|
const locator = page.locator(selector);
|
|
3289
3705
|
await MobileHumanize.humanClick(page, locator, { scrollIfNeeded: true });
|
|
@@ -3334,15 +3750,18 @@ var MobileHumanize = {
|
|
|
3334
3750
|
};
|
|
3335
3751
|
|
|
3336
3752
|
// src/humanize.js
|
|
3337
|
-
var
|
|
3753
|
+
var resolveDeviceFromPage2 = (page) => normalizeDevice(page?.[PageRuntimeStateKey]?.device);
|
|
3338
3754
|
var resolveDelegate = (page) => {
|
|
3339
|
-
return
|
|
3755
|
+
return resolveDeviceFromPage2(page) === Device.Mobile ? MobileHumanize : Humanize;
|
|
3340
3756
|
};
|
|
3341
3757
|
var callDelegate = (method, page, args) => {
|
|
3342
3758
|
const delegate = resolveDelegate(page);
|
|
3343
3759
|
return delegate[method](page, ...args);
|
|
3344
3760
|
};
|
|
3345
3761
|
var Humanize2 = {
|
|
3762
|
+
// M = Machine: native/mechanical operations for兼容路径。
|
|
3763
|
+
// 这组 API 不做人类化处理,只保留原生 click / focus / fill / type 等语义。
|
|
3764
|
+
M: MachineHumanize,
|
|
3346
3765
|
jitterMs(base, jitterPercent = 0.3) {
|
|
3347
3766
|
return Humanize.jitterMs(base, jitterPercent);
|
|
3348
3767
|
},
|
|
@@ -3371,6 +3790,12 @@ var Humanize2 = {
|
|
|
3371
3790
|
humanType(page, selector, text, options = {}) {
|
|
3372
3791
|
return callDelegate("humanType", page, [selector, text, options]);
|
|
3373
3792
|
},
|
|
3793
|
+
humanPress(page, targetOrKey, maybeKey, options = {}) {
|
|
3794
|
+
if (typeof maybeKey === "string") {
|
|
3795
|
+
return callDelegate("humanPress", page, [targetOrKey, maybeKey, options]);
|
|
3796
|
+
}
|
|
3797
|
+
return callDelegate("humanPress", page, [targetOrKey, maybeKey || options]);
|
|
3798
|
+
},
|
|
3374
3799
|
humanClear(page, selector) {
|
|
3375
3800
|
return callDelegate("humanClear", page, [selector]);
|
|
3376
3801
|
},
|
|
@@ -5842,7 +6267,7 @@ var Logger = {
|
|
|
5842
6267
|
};
|
|
5843
6268
|
|
|
5844
6269
|
// src/share.js
|
|
5845
|
-
var
|
|
6270
|
+
var import_delay4 = __toESM(require("delay"), 1);
|
|
5846
6271
|
|
|
5847
6272
|
// src/internals/watermarkify.js
|
|
5848
6273
|
var DEFAULT_TIMEZONE_OFFSET = 8;
|
|
@@ -5876,6 +6301,18 @@ var DEFAULT_STRIP_PROMPT_MAX_LINES = 3;
|
|
|
5876
6301
|
var DEFAULT_STRIP_CONTENT_SAFE_PADDING_X = 12;
|
|
5877
6302
|
var DEFAULT_STRIP_TEXT_WIDTH_SAFETY = 10;
|
|
5878
6303
|
var DEFAULT_BROWSER_COMPOSITOR_VIEWPORT_HEIGHT = 1200;
|
|
6304
|
+
var MOBILE_STRIP_HEIGHT = 104;
|
|
6305
|
+
var MOBILE_STRIP_PADDING_X = 14;
|
|
6306
|
+
var MOBILE_STRIP_PADDING_Y = 13;
|
|
6307
|
+
var MOBILE_STRIP_ROW_HEIGHT = 22;
|
|
6308
|
+
var MOBILE_STRIP_ROW_GAP = 7;
|
|
6309
|
+
var MOBILE_STRIP_LABEL_FONT_SIZE = 12.5;
|
|
6310
|
+
var MOBILE_STRIP_VALUE_FONT_SIZE = 13.5;
|
|
6311
|
+
var MOBILE_STRIP_LABEL_WIDTH = 45;
|
|
6312
|
+
var MOBILE_STRIP_PROMPT_LABEL_WIDTH = 60;
|
|
6313
|
+
var MOBILE_STRIP_LOC_LABEL_WIDTH = 30;
|
|
6314
|
+
var MOBILE_STRIP_IP_LABEL_WIDTH = 22;
|
|
6315
|
+
var MOBILE_STRIP_META_GAP = 12;
|
|
5879
6316
|
var WEAK_LOCATION_VALUES = /* @__PURE__ */ new Set(["cn", "\u4E2D\u56FD"]);
|
|
5880
6317
|
var LOCATION_NETWORK_SUFFIX_PATTERNS = [
|
|
5881
6318
|
/(?:中国)?移动$/i,
|
|
@@ -6660,10 +7097,16 @@ var resolveScreenshotWatermarkifyMeta = async (page, options = {}) => {
|
|
|
6660
7097
|
}),
|
|
6661
7098
|
watermark: options.watermark,
|
|
6662
7099
|
strip: options.strip,
|
|
6663
|
-
stripLogoSrc
|
|
7100
|
+
stripLogoSrc,
|
|
7101
|
+
device: resolvePageDevice(page)
|
|
6664
7102
|
};
|
|
6665
7103
|
};
|
|
6666
7104
|
var buildFontFamily = () => 'MiSans, "SF Pro Display", "PingFang SC", "Helvetica Neue", Arial, sans-serif';
|
|
7105
|
+
var resolvePageDevice = (page) => normalizeDevice(page?.[PageRuntimeStateKey]?.device);
|
|
7106
|
+
var findStripSegment = (segments, kind) => {
|
|
7107
|
+
const found = Array.isArray(segments) ? segments.find((segment) => segment?.kind === kind) : null;
|
|
7108
|
+
return found && typeof found === "object" ? found : {};
|
|
7109
|
+
};
|
|
6667
7110
|
var buildStripSegmentLayout = (segment, options = {}) => {
|
|
6668
7111
|
const label = normalizeWhitespace(segment?.label || "");
|
|
6669
7112
|
const rawValue = normalizeWhitespace(segment?.rawValue || segment?.value || "-") || "-";
|
|
@@ -6991,6 +7434,116 @@ var renderStripDivider = (x, centerY, height) => {
|
|
|
6991
7434
|
/>
|
|
6992
7435
|
`;
|
|
6993
7436
|
};
|
|
7437
|
+
var renderMobileStripText = ({
|
|
7438
|
+
label,
|
|
7439
|
+
value,
|
|
7440
|
+
labelX,
|
|
7441
|
+
y,
|
|
7442
|
+
valueWidth,
|
|
7443
|
+
labelWidth = MOBILE_STRIP_LABEL_WIDTH,
|
|
7444
|
+
fontFamily
|
|
7445
|
+
}) => {
|
|
7446
|
+
const safeLabel = normalizeWhitespace(label) || "-";
|
|
7447
|
+
const safeValue = fitTextWithEllipsis(
|
|
7448
|
+
Array.from(normalizeWhitespace(value) || "-"),
|
|
7449
|
+
Math.max(18, valueWidth),
|
|
7450
|
+
MOBILE_STRIP_VALUE_FONT_SIZE
|
|
7451
|
+
);
|
|
7452
|
+
return `
|
|
7453
|
+
<text
|
|
7454
|
+
x="${labelX}"
|
|
7455
|
+
y="${y}"
|
|
7456
|
+
fill="#111111"
|
|
7457
|
+
fill-opacity="0.54"
|
|
7458
|
+
font-family="${fontFamily}"
|
|
7459
|
+
font-size="${MOBILE_STRIP_LABEL_FONT_SIZE}"
|
|
7460
|
+
font-weight="650"
|
|
7461
|
+
dominant-baseline="middle"
|
|
7462
|
+
>${escapeXml(safeLabel)}</text>
|
|
7463
|
+
<text
|
|
7464
|
+
x="${labelX + labelWidth}"
|
|
7465
|
+
y="${y}"
|
|
7466
|
+
fill="#111111"
|
|
7467
|
+
font-family="${fontFamily}"
|
|
7468
|
+
font-size="${MOBILE_STRIP_VALUE_FONT_SIZE}"
|
|
7469
|
+
font-weight="730"
|
|
7470
|
+
dominant-baseline="middle"
|
|
7471
|
+
>${escapeXml(safeValue)}</text>
|
|
7472
|
+
`;
|
|
7473
|
+
};
|
|
7474
|
+
var renderMobileStrip = ({ meta, width, height, fontFamily }) => {
|
|
7475
|
+
const stripHeight = MOBILE_STRIP_HEIGHT;
|
|
7476
|
+
const stripY = height - stripHeight;
|
|
7477
|
+
const contentX = MOBILE_STRIP_PADDING_X;
|
|
7478
|
+
const contentWidth = Math.max(1, width - MOBILE_STRIP_PADDING_X * 2);
|
|
7479
|
+
const prompt = findStripSegment(meta.stripSegments, "prompt");
|
|
7480
|
+
const time = findStripSegment(meta.stripSegments, "time");
|
|
7481
|
+
const location = findStripSegment(meta.stripSegments, "location");
|
|
7482
|
+
const ip = findStripSegment(meta.stripSegments, "ip");
|
|
7483
|
+
const row1Y = stripY + MOBILE_STRIP_PADDING_Y + MOBILE_STRIP_ROW_HEIGHT / 2;
|
|
7484
|
+
const row2Y = row1Y + MOBILE_STRIP_ROW_HEIGHT + MOBILE_STRIP_ROW_GAP;
|
|
7485
|
+
const row3Y = row2Y + MOBILE_STRIP_ROW_HEIGHT + MOBILE_STRIP_ROW_GAP;
|
|
7486
|
+
const promptValueWidth = contentWidth - MOBILE_STRIP_PROMPT_LABEL_WIDTH;
|
|
7487
|
+
const locValueX = contentX + MOBILE_STRIP_LOC_LABEL_WIDTH;
|
|
7488
|
+
const ipLabelX = Math.min(
|
|
7489
|
+
width - MOBILE_STRIP_PADDING_X - MOBILE_STRIP_IP_LABEL_WIDTH - 70,
|
|
7490
|
+
Math.max(
|
|
7491
|
+
locValueX + 80 + MOBILE_STRIP_META_GAP,
|
|
7492
|
+
contentX + Math.round(contentWidth * 0.52)
|
|
7493
|
+
)
|
|
7494
|
+
);
|
|
7495
|
+
const locValueWidth = Math.max(
|
|
7496
|
+
28,
|
|
7497
|
+
ipLabelX - locValueX - MOBILE_STRIP_META_GAP
|
|
7498
|
+
);
|
|
7499
|
+
const ipValueWidth = Math.max(
|
|
7500
|
+
28,
|
|
7501
|
+
width - MOBILE_STRIP_PADDING_X - (ipLabelX + MOBILE_STRIP_IP_LABEL_WIDTH)
|
|
7502
|
+
);
|
|
7503
|
+
return `
|
|
7504
|
+
<g id="strip">
|
|
7505
|
+
<rect x="0" y="${stripY}" width="${width}" height="${stripHeight}" fill="#ffffff" fill-opacity="0.985" />
|
|
7506
|
+
<rect x="0" y="${stripY}" width="${width}" height="1" fill="#111111" fill-opacity="0.10" />
|
|
7507
|
+
<rect x="${contentX}" y="${stripY}" width="${Math.max(96, Math.round(width * 0.36))}" height="2" fill="url(#stripAccent)" />
|
|
7508
|
+
${renderMobileStripText({
|
|
7509
|
+
label: "Prompt",
|
|
7510
|
+
value: prompt.rawValue || prompt.value || "-",
|
|
7511
|
+
labelX: contentX,
|
|
7512
|
+
y: row1Y,
|
|
7513
|
+
valueWidth: promptValueWidth,
|
|
7514
|
+
labelWidth: MOBILE_STRIP_PROMPT_LABEL_WIDTH,
|
|
7515
|
+
fontFamily
|
|
7516
|
+
})}
|
|
7517
|
+
${renderMobileStripText({
|
|
7518
|
+
label: "Time",
|
|
7519
|
+
value: time.rawValue || time.value || "-",
|
|
7520
|
+
labelX: contentX,
|
|
7521
|
+
y: row2Y,
|
|
7522
|
+
valueWidth: promptValueWidth,
|
|
7523
|
+
labelWidth: MOBILE_STRIP_LABEL_WIDTH,
|
|
7524
|
+
fontFamily
|
|
7525
|
+
})}
|
|
7526
|
+
${renderMobileStripText({
|
|
7527
|
+
label: "Loc",
|
|
7528
|
+
value: location.rawValue || location.value || "-",
|
|
7529
|
+
labelX: contentX,
|
|
7530
|
+
y: row3Y,
|
|
7531
|
+
valueWidth: locValueWidth,
|
|
7532
|
+
labelWidth: MOBILE_STRIP_LOC_LABEL_WIDTH,
|
|
7533
|
+
fontFamily
|
|
7534
|
+
})}
|
|
7535
|
+
${renderMobileStripText({
|
|
7536
|
+
label: "IP",
|
|
7537
|
+
value: ip.rawValue || ip.value || "-",
|
|
7538
|
+
labelX: ipLabelX,
|
|
7539
|
+
y: row3Y,
|
|
7540
|
+
valueWidth: ipValueWidth,
|
|
7541
|
+
labelWidth: MOBILE_STRIP_IP_LABEL_WIDTH,
|
|
7542
|
+
fontFamily
|
|
7543
|
+
})}
|
|
7544
|
+
</g>
|
|
7545
|
+
`;
|
|
7546
|
+
};
|
|
6994
7547
|
var buildWatermarkifySvg = (meta, imageWidth, imageHeight) => {
|
|
6995
7548
|
const hasWatermark = meta?.watermark?.enabled !== false && normalizeText(meta?.watermarkText);
|
|
6996
7549
|
const hasStrip = meta?.strip?.enabled !== false && Array.isArray(meta?.stripSegments) && meta.stripSegments.length > 0;
|
|
@@ -7039,6 +7592,27 @@ var buildWatermarkifySvg = (meta, imageWidth, imageHeight) => {
|
|
|
7039
7592
|
parts.push(`<g id="watermarks">${stamps.join("")}</g>`);
|
|
7040
7593
|
}
|
|
7041
7594
|
if (hasStrip) {
|
|
7595
|
+
if (normalizeDevice(meta.device) === Device.Mobile) {
|
|
7596
|
+
parts.push(renderMobileStrip({
|
|
7597
|
+
meta,
|
|
7598
|
+
width,
|
|
7599
|
+
height,
|
|
7600
|
+
fontFamily
|
|
7601
|
+
}));
|
|
7602
|
+
return `
|
|
7603
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
|
|
7604
|
+
<defs>
|
|
7605
|
+
<linearGradient id="stripAccent" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
7606
|
+
<stop offset="0%" stop-color="#111111" stop-opacity="0.98" />
|
|
7607
|
+
<stop offset="24%" stop-color="#111111" stop-opacity="0.68" />
|
|
7608
|
+
<stop offset="58%" stop-color="#111111" stop-opacity="0.08" />
|
|
7609
|
+
<stop offset="100%" stop-color="#111111" stop-opacity="0" />
|
|
7610
|
+
</linearGradient>
|
|
7611
|
+
</defs>
|
|
7612
|
+
${parts.join("")}
|
|
7613
|
+
</svg>
|
|
7614
|
+
`;
|
|
7615
|
+
}
|
|
7042
7616
|
const logoSize = 28;
|
|
7043
7617
|
const logoX = DEFAULT_STRIP_PADDING_LEFT;
|
|
7044
7618
|
const contentStartX = logoX + logoSize + DEFAULT_STRIP_GAP;
|
|
@@ -7165,16 +7739,16 @@ var watermarkifyScreenshotBuffer = async (buffer, meta, page = null) => {
|
|
|
7165
7739
|
return await composeScreenshotBufferWithBrowser(page, buffer, overlaySvg, imageInfo);
|
|
7166
7740
|
};
|
|
7167
7741
|
|
|
7168
|
-
// src/internals/
|
|
7742
|
+
// src/internals/compression.js
|
|
7169
7743
|
var import_jimp = require("jimp");
|
|
7170
|
-
var logger14 = createInternalLogger("
|
|
7744
|
+
var logger14 = createInternalLogger("Compression");
|
|
7171
7745
|
var DEFAULT_SCREENSHOT_MAX_BYTES = 5 * 1024 * 1024;
|
|
7172
7746
|
var DEFAULT_SCREENSHOT_OUTPUT_TYPE = "jpeg";
|
|
7173
7747
|
var DEFAULT_SCREENSHOT_QUALITY = 0.72;
|
|
7174
7748
|
var DEFAULT_SCREENSHOT_MIN_QUALITY = 0.38;
|
|
7175
7749
|
var DEFAULT_SCREENSHOT_MIN_SCALE = 0.25;
|
|
7176
7750
|
var SUPPORTED_SCREENSHOT_OUTPUT_TYPES = /* @__PURE__ */ new Set(["jpeg"]);
|
|
7177
|
-
var
|
|
7751
|
+
var toPositiveInteger2 = (value, fallback = 0) => {
|
|
7178
7752
|
const number = Math.floor(Number(value) || 0);
|
|
7179
7753
|
return number > 0 ? number : fallback;
|
|
7180
7754
|
};
|
|
@@ -7196,7 +7770,7 @@ var normalizeScreenshotOutputType = (value) => {
|
|
|
7196
7770
|
};
|
|
7197
7771
|
var getBase64BytesFromBuffer = (buffer) => Math.ceil(buffer.length / 3) * 4;
|
|
7198
7772
|
var toJpegQuality = (value) => Math.round(normalizeQuality2(value, DEFAULT_SCREENSHOT_QUALITY) * 100);
|
|
7199
|
-
var
|
|
7773
|
+
var resolveImageCompression = (options = {}) => {
|
|
7200
7774
|
const explicit = options.compression;
|
|
7201
7775
|
const source = explicit && typeof explicit === "object" && !Array.isArray(explicit) ? explicit : {};
|
|
7202
7776
|
const enabled = explicit !== false && source.enabled !== false && options.compress !== false;
|
|
@@ -7210,7 +7784,7 @@ var resolveCaptureScreenCompression = (options = {}) => {
|
|
|
7210
7784
|
);
|
|
7211
7785
|
return {
|
|
7212
7786
|
enabled,
|
|
7213
|
-
maxBytes:
|
|
7787
|
+
maxBytes: toPositiveInteger2(
|
|
7214
7788
|
source.maxBytes ?? source.maxBase64Bytes ?? options.maxBytes ?? options.maxBase64Bytes,
|
|
7215
7789
|
DEFAULT_SCREENSHOT_MAX_BYTES
|
|
7216
7790
|
),
|
|
@@ -7247,7 +7821,7 @@ var encodeJpeg = async (sourceImage, compression, scale, quality) => {
|
|
|
7247
7821
|
format: compression.outputType
|
|
7248
7822
|
};
|
|
7249
7823
|
};
|
|
7250
|
-
var
|
|
7824
|
+
var compressImageBuffer = async (buffer, compression) => {
|
|
7251
7825
|
const sourceImage = await import_jimp.Jimp.read(buffer);
|
|
7252
7826
|
const maxQuality = toJpegQuality(compression.quality);
|
|
7253
7827
|
const minQuality = Math.min(maxQuality, toJpegQuality(compression.minQuality));
|
|
@@ -7280,12 +7854,12 @@ var compressScreenshotBuffer = async (buffer, compression) => {
|
|
|
7280
7854
|
const fallback = !smallest || finalCandidate.bytes < smallest.bytes ? finalCandidate : smallest;
|
|
7281
7855
|
return { ...fallback, withinLimit: fallback.bytes <= compression.maxBytes };
|
|
7282
7856
|
};
|
|
7283
|
-
var
|
|
7857
|
+
var compressImageBufferToBase64 = async (buffer, compression) => {
|
|
7284
7858
|
const originalBytes = getBase64BytesFromBuffer(buffer);
|
|
7285
7859
|
if (!compression.enabled || originalBytes <= compression.maxBytes) {
|
|
7286
7860
|
return buffer.toString("base64");
|
|
7287
7861
|
}
|
|
7288
|
-
const result = await
|
|
7862
|
+
const result = await compressImageBuffer(buffer, compression).catch((error) => {
|
|
7289
7863
|
logger14.warning(`captureScreen \u538B\u7F29\u5931\u8D25\uFF0C\u8FD4\u56DE\u539F\u56FE: ${error instanceof Error ? error.message : String(error)}`);
|
|
7290
7864
|
return null;
|
|
7291
7865
|
});
|
|
@@ -7684,7 +8258,7 @@ var Share = {
|
|
|
7684
8258
|
);
|
|
7685
8259
|
nextProgressLogTs = now + 5e3;
|
|
7686
8260
|
}
|
|
7687
|
-
await (0,
|
|
8261
|
+
await (0, import_delay4.default)(Math.max(0, Math.min(DEFAULT_POLL_INTERVAL_MS, remaining)));
|
|
7688
8262
|
}
|
|
7689
8263
|
if (share.mode === "response" && stats.responseMatched === 0) {
|
|
7690
8264
|
logger15.warning(
|
|
@@ -7722,71 +8296,27 @@ var Share = {
|
|
|
7722
8296
|
* @returns {Promise<string>} base64 image
|
|
7723
8297
|
*/
|
|
7724
8298
|
async captureScreen(page, options = {}) {
|
|
7725
|
-
const originalViewport = await resolveCurrentViewportSize(page);
|
|
7726
|
-
const defaultBuffer = Math.round((originalViewport.height || 1080) / 2);
|
|
7727
|
-
const buffer = options.buffer ?? defaultBuffer;
|
|
7728
8299
|
const restore = options.restore ?? false;
|
|
7729
|
-
const maxHeight = options.maxHeight ?? 8e3;
|
|
7730
8300
|
const screenshotWatermarkify = resolveCaptureScreenWatermarkify(page, options.watermarkify);
|
|
7731
|
-
const compression =
|
|
7732
|
-
|
|
7733
|
-
|
|
7734
|
-
|
|
7735
|
-
|
|
7736
|
-
|
|
7737
|
-
|
|
7738
|
-
|
|
7739
|
-
|
|
7740
|
-
|
|
7741
|
-
|
|
7742
|
-
|
|
7743
|
-
|
|
7744
|
-
|
|
7745
|
-
|
|
7746
|
-
el.style.overflow = "visible";
|
|
7747
|
-
el.style.height = "auto";
|
|
7748
|
-
el.style.maxHeight = "none";
|
|
7749
|
-
}
|
|
7750
|
-
});
|
|
7751
|
-
return maxHeight2;
|
|
7752
|
-
});
|
|
7753
|
-
const targetHeight = Math.min(maxScrollHeight + buffer, maxHeight);
|
|
7754
|
-
await page.setViewportSize({
|
|
7755
|
-
width: originalViewport.width,
|
|
7756
|
-
height: targetHeight
|
|
7757
|
-
});
|
|
7758
|
-
await (0, import_delay3.default)(1e3);
|
|
7759
|
-
const capturedAt = /* @__PURE__ */ new Date();
|
|
7760
|
-
const rawBuffer = await capturePageScreenshot(page, {
|
|
7761
|
-
fullPage: true,
|
|
7762
|
-
type: "png",
|
|
7763
|
-
maxClipHeight: targetHeight
|
|
8301
|
+
const compression = resolveImageCompression(options);
|
|
8302
|
+
const captureOptions = {
|
|
8303
|
+
buffer: options.buffer ?? 0,
|
|
8304
|
+
restore,
|
|
8305
|
+
maxHeight: options.maxHeight,
|
|
8306
|
+
type: "png",
|
|
8307
|
+
visibleOnly: true
|
|
8308
|
+
};
|
|
8309
|
+
const capturedAt = /* @__PURE__ */ new Date();
|
|
8310
|
+
const rawBuffer = await captureExpandedFullPageScreenshot(page, captureOptions);
|
|
8311
|
+
let outputBuffer = rawBuffer;
|
|
8312
|
+
if (screenshotWatermarkify.enabled) {
|
|
8313
|
+
const watermarkifyMeta = await resolveScreenshotWatermarkifyMeta(page, {
|
|
8314
|
+
...screenshotWatermarkify,
|
|
8315
|
+
capturedAt
|
|
7764
8316
|
});
|
|
7765
|
-
|
|
7766
|
-
if (screenshotWatermarkify.enabled) {
|
|
7767
|
-
const watermarkifyMeta = await resolveScreenshotWatermarkifyMeta(page, {
|
|
7768
|
-
...screenshotWatermarkify,
|
|
7769
|
-
capturedAt
|
|
7770
|
-
});
|
|
7771
|
-
outputBuffer = await watermarkifyScreenshotBuffer(rawBuffer, watermarkifyMeta, page);
|
|
7772
|
-
}
|
|
7773
|
-
return await compressScreenshotBufferToBase64(outputBuffer, compression);
|
|
7774
|
-
} finally {
|
|
7775
|
-
if (restore) {
|
|
7776
|
-
await page.evaluate(() => {
|
|
7777
|
-
document.querySelectorAll(".__pk_expanded__").forEach((el) => {
|
|
7778
|
-
el.style.overflow = el.dataset.pkOrigOverflow || "";
|
|
7779
|
-
el.style.height = el.dataset.pkOrigHeight || "";
|
|
7780
|
-
el.style.maxHeight = el.dataset.pkOrigMaxHeight || "";
|
|
7781
|
-
delete el.dataset.pkOrigOverflow;
|
|
7782
|
-
delete el.dataset.pkOrigHeight;
|
|
7783
|
-
delete el.dataset.pkOrigMaxHeight;
|
|
7784
|
-
el.classList.remove("__pk_expanded__");
|
|
7785
|
-
});
|
|
7786
|
-
});
|
|
7787
|
-
await page.setViewportSize(originalViewport);
|
|
7788
|
-
}
|
|
8317
|
+
outputBuffer = await watermarkifyScreenshotBuffer(rawBuffer, watermarkifyMeta, page);
|
|
7789
8318
|
}
|
|
8319
|
+
return await compressImageBufferToBase64(outputBuffer, compression);
|
|
7790
8320
|
}
|
|
7791
8321
|
};
|
|
7792
8322
|
|