@skrillex1224/playwright-toolkit 2.1.227 → 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/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 import_delay = __toESM(require("delay"), 1);
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, import_delay.default)(this.jitterMs(20 + Math.random() * 40, 0.2));
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, import_delay.default)(this.jitterMs(1e3));
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, import_delay.default)(this.jitterMs(reactionDelay, 0.4));
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, import_delay.default)(this.jitterMs(reactionDelay, 0.4));
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, import_delay.default)(ms);
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, import_delay.default)(this.jitterMs(600, 0.5));
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, import_delay.default)(this.jitterMs(200, 0.4));
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, import_delay.default)(charDelay);
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, import_delay.default)(pauseTime);
2665
+ await (0, import_delay2.default)(pauseTime);
2508
2666
  }
2509
2667
  }
2510
2668
  logger6.success("humanType");
@@ -2538,7 +2696,7 @@ var Humanize = {
2538
2696
  if (hasTarget) {
2539
2697
  await this.humanClick(page, targetOrKey, { reactionDelay: focusDelay, scrollIfNeeded, throwOnMissing });
2540
2698
  }
2541
- await (0, import_delay.default)(this.jitterMs(reactionDelay, 0.45));
2699
+ await (0, import_delay2.default)(this.jitterMs(reactionDelay, 0.45));
2542
2700
  await page.keyboard.press(key, {
2543
2701
  ...keyboardOptions,
2544
2702
  delay: this.jitterMs(holdDelay, 0.5)
@@ -2560,14 +2718,14 @@ var Humanize = {
2560
2718
  try {
2561
2719
  const locator = page.locator(selector);
2562
2720
  await locator.click();
2563
- await (0, import_delay.default)(this.jitterMs(200, 0.4));
2721
+ await (0, import_delay2.default)(this.jitterMs(200, 0.4));
2564
2722
  const currentValue = await locator.inputValue();
2565
2723
  if (!currentValue || currentValue.length === 0) {
2566
2724
  logger6.success("humanClear", "already empty");
2567
2725
  return;
2568
2726
  }
2569
2727
  await page.keyboard.press("Meta+A");
2570
- await (0, import_delay.default)(this.jitterMs(100, 0.4));
2728
+ await (0, import_delay2.default)(this.jitterMs(100, 0.4));
2571
2729
  await page.keyboard.press("Backspace");
2572
2730
  logger6.success("humanClear");
2573
2731
  } catch (error) {
@@ -2593,13 +2751,13 @@ var Humanize = {
2593
2751
  const x = 100 + Math.random() * (viewportSize.width - 200);
2594
2752
  const y = 100 + Math.random() * (viewportSize.height - 200);
2595
2753
  await cursor.actions.move({ x, y });
2596
- await (0, import_delay.default)(this.jitterMs(350, 0.4));
2754
+ await (0, import_delay2.default)(this.jitterMs(350, 0.4));
2597
2755
  } else if (action < 0.7) {
2598
2756
  const scrollY = (Math.random() - 0.5) * 200;
2599
2757
  await page.mouse.wheel(0, scrollY);
2600
- await (0, import_delay.default)(this.jitterMs(500, 0.4));
2758
+ await (0, import_delay2.default)(this.jitterMs(500, 0.4));
2601
2759
  } else {
2602
- await (0, import_delay.default)(this.jitterMs(800, 0.5));
2760
+ await (0, import_delay2.default)(this.jitterMs(800, 0.5));
2603
2761
  }
2604
2762
  }
2605
2763
  logger6.success("warmUpBrowsing");
@@ -2628,7 +2786,7 @@ var Humanize = {
2628
2786
  const scrollAmount = stepDistance * factor * sign * jitter;
2629
2787
  await page.mouse.wheel(0, scrollAmount);
2630
2788
  const baseDelay = 60 + i * 25;
2631
- await (0, import_delay.default)(this.jitterMs(baseDelay, 0.3));
2789
+ await (0, import_delay2.default)(this.jitterMs(baseDelay, 0.3));
2632
2790
  }
2633
2791
  logger6.success("naturalScroll");
2634
2792
  } catch (error) {
@@ -2774,7 +2932,7 @@ var MachineHumanize = {
2774
2932
  };
2775
2933
 
2776
2934
  // src/internals/humanize/shared.js
2777
- var import_delay2 = __toESM(require("delay"), 1);
2935
+ var import_delay3 = __toESM(require("delay"), 1);
2778
2936
  var jitterMs = (base, jitterPercent = 0.3) => {
2779
2937
  const jitter = Number(base || 0) * Number(jitterPercent || 0) * (Math.random() * 2 - 1);
2780
2938
  return Math.max(10, Math.round(Number(base || 0) + jitter));
@@ -2800,7 +2958,7 @@ var resolveElement = async (page, target, { throwOnMissing = true } = {}) => {
2800
2958
  }
2801
2959
  return element;
2802
2960
  };
2803
- var waitJitter = (base, jitterPercent = 0.3) => (0, import_delay2.default)(jitterMs(base, jitterPercent));
2961
+ var waitJitter = (base, jitterPercent = 0.3) => (0, import_delay3.default)(jitterMs(base, jitterPercent));
2804
2962
 
2805
2963
  // src/internals/humanize/mobile.js
2806
2964
  var logger7 = createInternalLogger("Humanize.Mobile");
@@ -3601,8 +3759,8 @@ var callDelegate = (method, page, args) => {
3601
3759
  return delegate[method](page, ...args);
3602
3760
  };
3603
3761
  var Humanize2 = {
3604
- // M = Machine: native/mechanical operations for compatibility paths.
3605
- // These APIs intentionally do not humanize timing, motion, or intent.
3762
+ // M = Machine: native/mechanical operations for兼容路径。
3763
+ // 这组 API 不做人类化处理,只保留原生 click / focus / fill / type 等语义。
3606
3764
  M: MachineHumanize,
3607
3765
  jitterMs(base, jitterPercent = 0.3) {
3608
3766
  return Humanize.jitterMs(base, jitterPercent);
@@ -6109,7 +6267,7 @@ var Logger = {
6109
6267
  };
6110
6268
 
6111
6269
  // src/share.js
6112
- var import_delay3 = __toESM(require("delay"), 1);
6270
+ var import_delay4 = __toESM(require("delay"), 1);
6113
6271
 
6114
6272
  // src/internals/watermarkify.js
6115
6273
  var DEFAULT_TIMEZONE_OFFSET = 8;
@@ -6143,6 +6301,18 @@ var DEFAULT_STRIP_PROMPT_MAX_LINES = 3;
6143
6301
  var DEFAULT_STRIP_CONTENT_SAFE_PADDING_X = 12;
6144
6302
  var DEFAULT_STRIP_TEXT_WIDTH_SAFETY = 10;
6145
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;
6146
6316
  var WEAK_LOCATION_VALUES = /* @__PURE__ */ new Set(["cn", "\u4E2D\u56FD"]);
6147
6317
  var LOCATION_NETWORK_SUFFIX_PATTERNS = [
6148
6318
  /(?:中国)?移动$/i,
@@ -6927,10 +7097,16 @@ var resolveScreenshotWatermarkifyMeta = async (page, options = {}) => {
6927
7097
  }),
6928
7098
  watermark: options.watermark,
6929
7099
  strip: options.strip,
6930
- stripLogoSrc
7100
+ stripLogoSrc,
7101
+ device: resolvePageDevice(page)
6931
7102
  };
6932
7103
  };
6933
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
+ };
6934
7110
  var buildStripSegmentLayout = (segment, options = {}) => {
6935
7111
  const label = normalizeWhitespace(segment?.label || "");
6936
7112
  const rawValue = normalizeWhitespace(segment?.rawValue || segment?.value || "-") || "-";
@@ -7258,6 +7434,116 @@ var renderStripDivider = (x, centerY, height) => {
7258
7434
  />
7259
7435
  `;
7260
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
+ };
7261
7547
  var buildWatermarkifySvg = (meta, imageWidth, imageHeight) => {
7262
7548
  const hasWatermark = meta?.watermark?.enabled !== false && normalizeText(meta?.watermarkText);
7263
7549
  const hasStrip = meta?.strip?.enabled !== false && Array.isArray(meta?.stripSegments) && meta.stripSegments.length > 0;
@@ -7306,6 +7592,27 @@ var buildWatermarkifySvg = (meta, imageWidth, imageHeight) => {
7306
7592
  parts.push(`<g id="watermarks">${stamps.join("")}</g>`);
7307
7593
  }
7308
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
+ }
7309
7616
  const logoSize = 28;
7310
7617
  const logoX = DEFAULT_STRIP_PADDING_LEFT;
7311
7618
  const contentStartX = logoX + logoSize + DEFAULT_STRIP_GAP;
@@ -7432,16 +7739,16 @@ var watermarkifyScreenshotBuffer = async (buffer, meta, page = null) => {
7432
7739
  return await composeScreenshotBufferWithBrowser(page, buffer, overlaySvg, imageInfo);
7433
7740
  };
7434
7741
 
7435
- // src/internals/screenshot-compression.js
7742
+ // src/internals/compression.js
7436
7743
  var import_jimp = require("jimp");
7437
- var logger14 = createInternalLogger("ScreenshotCompression");
7744
+ var logger14 = createInternalLogger("Compression");
7438
7745
  var DEFAULT_SCREENSHOT_MAX_BYTES = 5 * 1024 * 1024;
7439
7746
  var DEFAULT_SCREENSHOT_OUTPUT_TYPE = "jpeg";
7440
7747
  var DEFAULT_SCREENSHOT_QUALITY = 0.72;
7441
7748
  var DEFAULT_SCREENSHOT_MIN_QUALITY = 0.38;
7442
7749
  var DEFAULT_SCREENSHOT_MIN_SCALE = 0.25;
7443
7750
  var SUPPORTED_SCREENSHOT_OUTPUT_TYPES = /* @__PURE__ */ new Set(["jpeg"]);
7444
- var toPositiveInteger = (value, fallback = 0) => {
7751
+ var toPositiveInteger2 = (value, fallback = 0) => {
7445
7752
  const number = Math.floor(Number(value) || 0);
7446
7753
  return number > 0 ? number : fallback;
7447
7754
  };
@@ -7463,7 +7770,7 @@ var normalizeScreenshotOutputType = (value) => {
7463
7770
  };
7464
7771
  var getBase64BytesFromBuffer = (buffer) => Math.ceil(buffer.length / 3) * 4;
7465
7772
  var toJpegQuality = (value) => Math.round(normalizeQuality2(value, DEFAULT_SCREENSHOT_QUALITY) * 100);
7466
- var resolveCaptureScreenCompression = (options = {}) => {
7773
+ var resolveImageCompression = (options = {}) => {
7467
7774
  const explicit = options.compression;
7468
7775
  const source = explicit && typeof explicit === "object" && !Array.isArray(explicit) ? explicit : {};
7469
7776
  const enabled = explicit !== false && source.enabled !== false && options.compress !== false;
@@ -7477,7 +7784,7 @@ var resolveCaptureScreenCompression = (options = {}) => {
7477
7784
  );
7478
7785
  return {
7479
7786
  enabled,
7480
- maxBytes: toPositiveInteger(
7787
+ maxBytes: toPositiveInteger2(
7481
7788
  source.maxBytes ?? source.maxBase64Bytes ?? options.maxBytes ?? options.maxBase64Bytes,
7482
7789
  DEFAULT_SCREENSHOT_MAX_BYTES
7483
7790
  ),
@@ -7514,7 +7821,7 @@ var encodeJpeg = async (sourceImage, compression, scale, quality) => {
7514
7821
  format: compression.outputType
7515
7822
  };
7516
7823
  };
7517
- var compressScreenshotBuffer = async (buffer, compression) => {
7824
+ var compressImageBuffer = async (buffer, compression) => {
7518
7825
  const sourceImage = await import_jimp.Jimp.read(buffer);
7519
7826
  const maxQuality = toJpegQuality(compression.quality);
7520
7827
  const minQuality = Math.min(maxQuality, toJpegQuality(compression.minQuality));
@@ -7547,12 +7854,12 @@ var compressScreenshotBuffer = async (buffer, compression) => {
7547
7854
  const fallback = !smallest || finalCandidate.bytes < smallest.bytes ? finalCandidate : smallest;
7548
7855
  return { ...fallback, withinLimit: fallback.bytes <= compression.maxBytes };
7549
7856
  };
7550
- var compressScreenshotBufferToBase64 = async (buffer, compression) => {
7857
+ var compressImageBufferToBase64 = async (buffer, compression) => {
7551
7858
  const originalBytes = getBase64BytesFromBuffer(buffer);
7552
7859
  if (!compression.enabled || originalBytes <= compression.maxBytes) {
7553
7860
  return buffer.toString("base64");
7554
7861
  }
7555
- const result = await compressScreenshotBuffer(buffer, compression).catch((error) => {
7862
+ const result = await compressImageBuffer(buffer, compression).catch((error) => {
7556
7863
  logger14.warning(`captureScreen \u538B\u7F29\u5931\u8D25\uFF0C\u8FD4\u56DE\u539F\u56FE: ${error instanceof Error ? error.message : String(error)}`);
7557
7864
  return null;
7558
7865
  });
@@ -7951,7 +8258,7 @@ var Share = {
7951
8258
  );
7952
8259
  nextProgressLogTs = now + 5e3;
7953
8260
  }
7954
- await (0, import_delay3.default)(Math.max(0, Math.min(DEFAULT_POLL_INTERVAL_MS, remaining)));
8261
+ await (0, import_delay4.default)(Math.max(0, Math.min(DEFAULT_POLL_INTERVAL_MS, remaining)));
7955
8262
  }
7956
8263
  if (share.mode === "response" && stats.responseMatched === 0) {
7957
8264
  logger15.warning(
@@ -7989,71 +8296,27 @@ var Share = {
7989
8296
  * @returns {Promise<string>} base64 image
7990
8297
  */
7991
8298
  async captureScreen(page, options = {}) {
7992
- const originalViewport = await resolveCurrentViewportSize(page);
7993
- const defaultBuffer = Math.round((originalViewport.height || 1080) / 2);
7994
- const buffer = options.buffer ?? defaultBuffer;
7995
8299
  const restore = options.restore ?? false;
7996
- const maxHeight = options.maxHeight ?? 8e3;
7997
8300
  const screenshotWatermarkify = resolveCaptureScreenWatermarkify(page, options.watermarkify);
7998
- const compression = resolveCaptureScreenCompression(options);
7999
- try {
8000
- const maxScrollHeight = await page.evaluate(() => {
8001
- let maxHeight2 = document.body.scrollHeight;
8002
- document.querySelectorAll("*").forEach((el) => {
8003
- const style = window.getComputedStyle(el);
8004
- const overflowY = style.overflowY;
8005
- if ((overflowY === "auto" || overflowY === "scroll") && el.scrollHeight > el.clientHeight) {
8006
- if (el.scrollHeight > maxHeight2) {
8007
- maxHeight2 = el.scrollHeight;
8008
- }
8009
- el.dataset.pkOrigOverflow = el.style.overflow;
8010
- el.dataset.pkOrigHeight = el.style.height;
8011
- el.dataset.pkOrigMaxHeight = el.style.maxHeight;
8012
- el.classList.add("__pk_expanded__");
8013
- el.style.overflow = "visible";
8014
- el.style.height = "auto";
8015
- el.style.maxHeight = "none";
8016
- }
8017
- });
8018
- return maxHeight2;
8019
- });
8020
- const targetHeight = Math.min(maxScrollHeight + buffer, maxHeight);
8021
- await page.setViewportSize({
8022
- width: originalViewport.width,
8023
- height: targetHeight
8024
- });
8025
- await (0, import_delay3.default)(1e3);
8026
- const capturedAt = /* @__PURE__ */ new Date();
8027
- const rawBuffer = await capturePageScreenshot(page, {
8028
- fullPage: true,
8029
- type: "png",
8030
- 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
8031
8316
  });
8032
- let outputBuffer = rawBuffer;
8033
- if (screenshotWatermarkify.enabled) {
8034
- const watermarkifyMeta = await resolveScreenshotWatermarkifyMeta(page, {
8035
- ...screenshotWatermarkify,
8036
- capturedAt
8037
- });
8038
- outputBuffer = await watermarkifyScreenshotBuffer(rawBuffer, watermarkifyMeta, page);
8039
- }
8040
- return await compressScreenshotBufferToBase64(outputBuffer, compression);
8041
- } finally {
8042
- if (restore) {
8043
- await page.evaluate(() => {
8044
- document.querySelectorAll(".__pk_expanded__").forEach((el) => {
8045
- el.style.overflow = el.dataset.pkOrigOverflow || "";
8046
- el.style.height = el.dataset.pkOrigHeight || "";
8047
- el.style.maxHeight = el.dataset.pkOrigMaxHeight || "";
8048
- delete el.dataset.pkOrigOverflow;
8049
- delete el.dataset.pkOrigHeight;
8050
- delete el.dataset.pkOrigMaxHeight;
8051
- el.classList.remove("__pk_expanded__");
8052
- });
8053
- });
8054
- await page.setViewportSize(originalViewport);
8055
- }
8317
+ outputBuffer = await watermarkifyScreenshotBuffer(rawBuffer, watermarkifyMeta, page);
8056
8318
  }
8319
+ return await compressImageBufferToBase64(outputBuffer, compression);
8057
8320
  }
8058
8321
  };
8059
8322