@skrillex1224/playwright-toolkit 2.1.210 → 2.1.211

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.js CHANGED
@@ -408,6 +408,33 @@ function createInternalLogger(moduleName, explicitLogger) {
408
408
  };
409
409
  }
410
410
 
411
+ // src/internals/viewport.js
412
+ var toPositiveInt = (value) => {
413
+ const number = Math.round(Number(value) || 0);
414
+ return number > 0 ? number : 0;
415
+ };
416
+ var resolveCurrentViewportSize = async (page, fallback = {}) => {
417
+ const directViewport = page?.viewportSize?.();
418
+ const directWidth = toPositiveInt(directViewport?.width);
419
+ const directHeight = toPositiveInt(directViewport?.height);
420
+ if (directWidth && directHeight) {
421
+ return {
422
+ width: directWidth,
423
+ height: directHeight
424
+ };
425
+ }
426
+ const measuredViewport = await page?.evaluate?.(() => ({
427
+ width: window.innerWidth || document.documentElement?.clientWidth || document.body?.clientWidth || 0,
428
+ height: window.innerHeight || document.documentElement?.clientHeight || document.body?.clientHeight || 0
429
+ })).catch(() => null);
430
+ const fallbackWidth = toPositiveInt(fallback?.width);
431
+ const fallbackHeight = toPositiveInt(fallback?.height);
432
+ return {
433
+ width: toPositiveInt(measuredViewport?.width) || fallbackWidth || 1,
434
+ height: toPositiveInt(measuredViewport?.height) || fallbackHeight || 1
435
+ };
436
+ };
437
+
411
438
  // src/internals/screenshot.js
412
439
  var logger = createInternalLogger("Screenshot");
413
440
  var DEFAULT_TIMEOUT_MS = 5e3;
@@ -434,7 +461,7 @@ var normalizeQuality = (value, type) => {
434
461
  };
435
462
  var buildFullPageClip = (metrics, viewport, maxClipHeight) => {
436
463
  const contentSize = metrics && typeof metrics === "object" ? metrics.contentSize || null : null;
437
- const width = Math.max(1, Math.ceil(contentSize?.width || viewport.width || 1));
464
+ const width = Math.max(1, Math.ceil(viewport.width || contentSize?.width || 1));
438
465
  let height = Math.max(1, Math.ceil(contentSize?.height || viewport.height || 1));
439
466
  if (maxClipHeight > 0) {
440
467
  height = Math.min(height, maxClipHeight);
@@ -471,7 +498,7 @@ var capturePageScreenshot = async (page, options = {}) => {
471
498
  const session = await context.newCDPSession(page);
472
499
  try {
473
500
  const metrics = await session.send("Page.getLayoutMetrics");
474
- const viewport = page.viewportSize() || { width: 1280, height: 720 };
501
+ const viewport = await resolveCurrentViewportSize(page);
475
502
  const captureParams = {
476
503
  format: type,
477
504
  fromSurface: true,
@@ -924,6 +951,9 @@ var ProxyMeterRuntime = {
924
951
  getProxyMeterSnapshot
925
952
  };
926
953
 
954
+ // src/internals/constants.js
955
+ var PageRuntimeStateKey = "__playwright_toolkit_runtime_state__";
956
+
927
957
  // src/runtime-env.js
928
958
  var BROWSER_PROFILE_SCHEMA_VERSION = 1;
929
959
  var rememberedRuntimeState = null;
@@ -1430,6 +1460,7 @@ var RuntimeEnv = {
1430
1460
  parseInput(input = {}, actor = "") {
1431
1461
  const runtime2 = tryParseJSON(input?.runtime) || {};
1432
1462
  const resolvedActor = String(actor || input?.actor || "").trim();
1463
+ const query = String(input?.query || "").trim();
1433
1464
  const cookies = normalizeCookies(runtime2?.cookies);
1434
1465
  const cookieMap = buildCookieMap(cookies);
1435
1466
  const localStorage = normalizeLocalStorage(runtime2?.local_storage);
@@ -1453,6 +1484,7 @@ var RuntimeEnv = {
1453
1484
  actor: resolvedActor,
1454
1485
  runtime: normalizedRuntime,
1455
1486
  envId,
1487
+ query,
1456
1488
  auth,
1457
1489
  cookies,
1458
1490
  cookieMap,
@@ -1520,6 +1552,12 @@ var RuntimeEnv = {
1520
1552
  async applyToPage(page, source = {}, options = {}) {
1521
1553
  if (!page) return;
1522
1554
  const state = normalizeRuntimeState(source, options?.actor || "");
1555
+ Object.defineProperty(page, PageRuntimeStateKey, {
1556
+ configurable: true,
1557
+ enumerable: false,
1558
+ writable: true,
1559
+ value: state
1560
+ });
1523
1561
  const localStorage = state.localStorage || {};
1524
1562
  const sessionStorage = state.sessionStorage || {};
1525
1563
  const cookies = (state.cookies || []).map((cookie) => {
@@ -4532,11 +4570,26 @@ var DEFAULT_WATERMARK_CELL_HEIGHT = 330;
4532
4570
  var DEFAULT_STRIP_LOGO_URL = "https://static.heartbitai.com/geo/icon/favicon.png";
4533
4571
  var DEFAULT_IP_LOOKUP_URL = "http://myip.ipip.net";
4534
4572
  var DEFAULT_LOGO_FETCH_TIMEOUT_MS = 2500;
4535
- var DEFAULT_STRIP_HEIGHT = 78;
4536
- var DEFAULT_STRIP_PADDING_LEFT = 20;
4537
- var DEFAULT_STRIP_PADDING_RIGHT = 22;
4538
- var DEFAULT_STRIP_GAP = 16;
4573
+ var DEFAULT_STRIP_ONE_LINE_HEIGHT = 78;
4574
+ var DEFAULT_STRIP_WRAPPED_MIN_HEIGHT = 108;
4575
+ var DEFAULT_STRIP_PADDING_LEFT = 22;
4576
+ var DEFAULT_STRIP_PADDING_RIGHT = 28;
4577
+ var DEFAULT_STRIP_GAP = 18;
4539
4578
  var DEFAULT_STRIP_LABEL_WIDTH = 56;
4579
+ var DEFAULT_STRIP_PROMPT_MAX_SAFE_CHARS = 180;
4580
+ var DEFAULT_STRIP_PROMPT_MIN_TOTAL_WIDTH = 320;
4581
+ var DEFAULT_STRIP_PROMPT_PREFERRED_WIDTH_RATIO = 0.58;
4582
+ var DEFAULT_STRIP_ZONE_GAP = 18;
4583
+ var DEFAULT_STRIP_PROMPT_TRAILING_PADDING = 16;
4584
+ var DEFAULT_STRIP_LABEL_FONT_SIZE = 14.5;
4585
+ var DEFAULT_STRIP_VALUE_FONT_SIZE = 15.5;
4586
+ var DEFAULT_STRIP_VALUE_PADDING = 10;
4587
+ var DEFAULT_STRIP_LABEL_PADDING = 14;
4588
+ var DEFAULT_STRIP_SECTION_GAP = DEFAULT_STRIP_GAP + 16;
4589
+ var DEFAULT_STRIP_LINE_HEIGHT_RATIO = 1.28;
4590
+ var DEFAULT_STRIP_PROMPT_MAX_LINES = 3;
4591
+ var DEFAULT_STRIP_CONTENT_SAFE_PADDING_X = 12;
4592
+ var DEFAULT_STRIP_TEXT_WIDTH_SAFETY = 10;
4540
4593
  var WEAK_LOCATION_VALUES = /* @__PURE__ */ new Set(["cn", "\u4E2D\u56FD"]);
4541
4594
  var LOCATION_NETWORK_SUFFIX_PATTERNS = [
4542
4595
  /(?:中国)?移动$/i,
@@ -4561,6 +4614,7 @@ var LOCATION_NETWORK_SUFFIX_PATTERNS = [
4561
4614
  /digitalocean$/i
4562
4615
  ];
4563
4616
  var cachedStripLogoSrcPromise = null;
4617
+ var cachedEnrichmentByContext = /* @__PURE__ */ new WeakMap();
4564
4618
  var normalizeText = (value) => String(value || "").trim();
4565
4619
  var toInline = (value, maxLen = 200) => {
4566
4620
  const text = normalizeText(value);
@@ -4572,6 +4626,15 @@ var shortenTail = (value, maxLen = 80) => {
4572
4626
  if (!text || text.length <= maxLen) return text;
4573
4627
  return `${text.slice(0, Math.max(0, maxLen - 1)).trimEnd()}\u2026`;
4574
4628
  };
4629
+ var shortenByCharacters = (value, maxChars = 80) => {
4630
+ const text = normalizeWhitespace(value);
4631
+ if (!text) return "";
4632
+ const chars = Array.from(text);
4633
+ if (chars.length <= maxChars) {
4634
+ return text;
4635
+ }
4636
+ return `${chars.slice(0, Math.max(0, maxChars - 1)).join("").trimEnd()}\u2026`;
4637
+ };
4575
4638
  var padDatePart = (value) => String(value).padStart(2, "0");
4576
4639
  var formatUtcOffsetLabel = (offsetHours = DEFAULT_TIMEZONE_OFFSET) => {
4577
4640
  const sign = offsetHours >= 0 ? "+" : "-";
@@ -4644,6 +4707,29 @@ var fillEnrichment = (target, source) => {
4644
4707
  }
4645
4708
  return target;
4646
4709
  };
4710
+ var readCachedEnrichment = (page) => {
4711
+ const context = page && typeof page.context === "function" ? page.context() : null;
4712
+ if (!context) {
4713
+ return null;
4714
+ }
4715
+ const cached = cachedEnrichmentByContext.get(context);
4716
+ return cached && typeof cached === "object" ? cached : null;
4717
+ };
4718
+ var writeCachedEnrichment = (page, source) => {
4719
+ const context = page && typeof page.context === "function" ? page.context() : null;
4720
+ if (!context || !source || typeof source !== "object") {
4721
+ return;
4722
+ }
4723
+ const current = cachedEnrichmentByContext.get(context) || {};
4724
+ const next = {
4725
+ ip: toInline(source.ip || current.ip, 80),
4726
+ location: toInline(source.location || current.location, 80)
4727
+ };
4728
+ if (!next.ip && !next.location) {
4729
+ return;
4730
+ }
4731
+ cachedEnrichmentByContext.set(context, next);
4732
+ };
4647
4733
  var getHostname = (url) => {
4648
4734
  try {
4649
4735
  const parsed = new URL(url);
@@ -4773,44 +4859,43 @@ var openProbePage = async (page) => {
4773
4859
  if (!context) {
4774
4860
  return null;
4775
4861
  }
4862
+ const browser = typeof context.browser === "function" ? context.browser() : null;
4863
+ if (browser && typeof browser.newContext === "function") {
4864
+ const probeContext = await browser.newContext().catch(() => null);
4865
+ if (probeContext && typeof probeContext.newPage === "function") {
4866
+ const probePage = await probeContext.newPage().catch(async () => {
4867
+ await probeContext.close().catch(() => {
4868
+ });
4869
+ return null;
4870
+ });
4871
+ if (probePage) {
4872
+ return {
4873
+ page: probePage,
4874
+ close: async () => {
4875
+ await probeContext.close().catch(() => {
4876
+ });
4877
+ }
4878
+ };
4879
+ }
4880
+ } else {
4881
+ await probeContext?.close?.().catch(() => {
4882
+ });
4883
+ }
4884
+ }
4776
4885
  if (typeof context.newPage === "function") {
4777
4886
  try {
4778
- const probePage2 = await context.newPage();
4887
+ const probePage = await context.newPage();
4779
4888
  return {
4780
- page: probePage2,
4889
+ page: probePage,
4781
4890
  close: async () => {
4782
- await probePage2.close().catch(() => {
4891
+ await probePage.close().catch(() => {
4783
4892
  });
4784
4893
  }
4785
4894
  };
4786
4895
  } catch {
4787
4896
  }
4788
4897
  }
4789
- const browser = typeof context.browser === "function" ? context.browser() : null;
4790
- if (!browser || typeof browser.newContext !== "function") {
4791
- return null;
4792
- }
4793
- const probeContext = await browser.newContext().catch(() => null);
4794
- if (!probeContext || typeof probeContext.newPage !== "function") {
4795
- await probeContext?.close?.().catch(() => {
4796
- });
4797
- return null;
4798
- }
4799
- const probePage = await probeContext.newPage().catch(async () => {
4800
- await probeContext.close().catch(() => {
4801
- });
4802
- return null;
4803
- });
4804
- if (!probePage) {
4805
- return null;
4806
- }
4807
- return {
4808
- page: probePage,
4809
- close: async () => {
4810
- await probeContext.close().catch(() => {
4811
- });
4812
- }
4813
- };
4898
+ return null;
4814
4899
  };
4815
4900
  var resolveWithIpLookup = async (page, options = {}) => {
4816
4901
  if (!page || typeof page.context !== "function" || options.ipLookup === false) {
@@ -4855,10 +4940,14 @@ var resolveWithIpLookup = async (page, options = {}) => {
4855
4940
  };
4856
4941
  var resolveEnrichment = async (page, baseMeta, options) => {
4857
4942
  const response = options.response && typeof options.response === "object" ? options.response : null;
4943
+ const cached = readCachedEnrichment(page);
4858
4944
  const merged = {
4859
4945
  ip: toInline(options.ip, 80),
4860
4946
  location: toInline(options.location, 80)
4861
4947
  };
4948
+ if (!merged.ip || !merged.location) {
4949
+ fillEnrichment(merged, cached);
4950
+ }
4862
4951
  if (!merged.ip || !merged.location) {
4863
4952
  fillEnrichment(
4864
4953
  merged,
@@ -4882,6 +4971,7 @@ var resolveEnrichment = async (page, baseMeta, options) => {
4882
4971
  merged.location = headerLocation || merged.location;
4883
4972
  }
4884
4973
  }
4974
+ writeCachedEnrichment(page, merged);
4885
4975
  return merged;
4886
4976
  };
4887
4977
  var buildWatermarkStamp = ({ prompt, captureTime, ip, location }) => {
@@ -4894,7 +4984,12 @@ var buildWatermarkStamp = ({ prompt, captureTime, ip, location }) => {
4894
4984
  return parts.join(" | ");
4895
4985
  };
4896
4986
  var buildStripSegments = ({ prompt, captureTime, ip, location }) => {
4897
- const promptValue = toInline(prompt, 240) || "-";
4987
+ const promptValue = shortenByCharacters(
4988
+ normalizeWhitespace(prompt) || "-",
4989
+ DEFAULT_STRIP_PROMPT_MAX_SAFE_CHARS
4990
+ ) || "-";
4991
+ const ipValue = normalizeWhitespace(ip) || "-";
4992
+ const locationValue = normalizeWhitespace(location) || "-";
4898
4993
  return [
4899
4994
  {
4900
4995
  kind: "prompt",
@@ -4911,14 +5006,14 @@ var buildStripSegments = ({ prompt, captureTime, ip, location }) => {
4911
5006
  {
4912
5007
  kind: "location",
4913
5008
  label: "Loc",
4914
- value: shortenTail(location, 24) || "-",
4915
- rawValue: toInline(location, 80) || "-"
5009
+ value: locationValue,
5010
+ rawValue: locationValue
4916
5011
  },
4917
5012
  {
4918
5013
  kind: "ip",
4919
5014
  label: "IP",
4920
- value: toInline(ip, 28) || "-",
4921
- rawValue: toInline(ip, 80) || "-"
5015
+ value: ipValue,
5016
+ rawValue: ipValue
4922
5017
  }
4923
5018
  ];
4924
5019
  };
@@ -4939,28 +5034,9 @@ var estimateTextWidth = (value, fontSize = 16) => {
4939
5034
  }
4940
5035
  return Math.ceil(width);
4941
5036
  };
4942
- var fitTextToWidth = (value, maxWidth, fontSize = 16) => {
4943
- const text = normalizeWhitespace(value);
4944
- if (!text) return "-";
4945
- const safeMaxWidth = Math.max(fontSize * 3, Number(maxWidth) || 0);
4946
- if (estimateTextWidth(text, fontSize) <= safeMaxWidth) {
4947
- return text;
4948
- }
4949
- const ellipsis = "\u2026";
4950
- const ellipsisWidth = estimateTextWidth(ellipsis, fontSize);
4951
- let current = "";
4952
- for (const char of text) {
4953
- const next = current + char;
4954
- if (estimateTextWidth(next, fontSize) + ellipsisWidth > safeMaxWidth) {
4955
- break;
4956
- }
4957
- current = next;
4958
- }
4959
- return `${current.trimEnd()}${ellipsis}`;
4960
- };
4961
5037
  var resolvePromptFields = (options = {}, fallbackTitle = "") => {
4962
5038
  const query = normalizeText(options.query);
4963
- const prompt = normalizeText(options.prompt) || query || normalizeText(fallbackTitle) || "\u672A\u63D0\u4F9B Prompt";
5039
+ const prompt = query || normalizeText(options.prompt) || normalizeText(fallbackTitle) || "\u672A\u63D0\u4F9B Prompt";
4964
5040
  return {
4965
5041
  prompt,
4966
5042
  query: query || prompt
@@ -5016,8 +5092,8 @@ var normalizeScreenshotWatermarkify = (value) => {
5016
5092
  response: source.response ?? null,
5017
5093
  ip: normalizeText(source.ip),
5018
5094
  location: normalizeText(source.location),
5019
- prompt: toInline(source.prompt, 220),
5020
- query: toInline(source.query, 220),
5095
+ prompt: normalizeWhitespace(source.prompt),
5096
+ query: normalizeWhitespace(source.query),
5021
5097
  ipLookup: source.ipLookup !== false,
5022
5098
  ipLookupTimeoutMs: Number.isFinite(ipLookupTimeoutMsRaw) ? ipLookupTimeoutMsRaw : DEFAULT_IP_LOOKUP_TIMEOUT_MS,
5023
5099
  resolver: typeof source.resolver === "function" ? source.resolver : null,
@@ -5070,53 +5146,332 @@ var resolveScreenshotWatermarkifyMeta = async (page, options = {}) => {
5070
5146
  };
5071
5147
  };
5072
5148
  var buildFontFamily = () => 'MiSans, "SF Pro Display", "PingFang SC", "Helvetica Neue", Arial, sans-serif';
5073
- var buildStripSegmentLayouts = (segments, contentWidth) => {
5149
+ var buildStripSegmentLayout = (segment, options = {}) => {
5150
+ const label = normalizeWhitespace(segment?.label || "");
5151
+ const rawValue = normalizeWhitespace(segment?.rawValue || segment?.value || "-") || "-";
5152
+ const labelFontSize = DEFAULT_STRIP_LABEL_FONT_SIZE;
5153
+ const valueFontSize = DEFAULT_STRIP_VALUE_FONT_SIZE;
5154
+ const labelWidth = Math.max(
5155
+ DEFAULT_STRIP_LABEL_WIDTH,
5156
+ estimateTextWidth(label, labelFontSize) + DEFAULT_STRIP_LABEL_PADDING
5157
+ );
5158
+ const rawValueWidth = Math.max(
5159
+ 24,
5160
+ estimateTextWidth(rawValue, valueFontSize) + DEFAULT_STRIP_VALUE_PADDING
5161
+ );
5162
+ const maxValueWidth = Math.max(0, Number(options.valueMaxWidth) || 0);
5163
+ const renderValue = maxValueWidth > 0 ? fitTextWithEllipsis(rawValue, Math.max(18, maxValueWidth - DEFAULT_STRIP_VALUE_PADDING), valueFontSize) : rawValue;
5164
+ const valueWidth = maxValueWidth > 0 ? Math.max(24, Math.min(maxValueWidth, estimateTextWidth(renderValue, valueFontSize) + DEFAULT_STRIP_VALUE_PADDING)) : rawValueWidth;
5165
+ return {
5166
+ ...segment,
5167
+ label,
5168
+ rawValue,
5169
+ renderValue,
5170
+ labelFontSize,
5171
+ valueFontSize,
5172
+ labelWidth,
5173
+ valueWidth,
5174
+ segmentWidth: labelWidth + valueWidth,
5175
+ rowHeight: Math.round(Math.max(labelFontSize, valueFontSize) * 1.42)
5176
+ };
5177
+ };
5178
+ var buildStripRowLayout = (segments, overrides = {}) => {
5074
5179
  const safeSegments = Array.isArray(segments) ? segments : [];
5075
- const sectionGap = DEFAULT_STRIP_GAP + 16;
5076
- const labelWidth = DEFAULT_STRIP_LABEL_WIDTH;
5077
- const promptFontSize = 15;
5078
- const otherFontSizes = {
5079
- time: 17,
5080
- location: 17,
5081
- ip: 17
5180
+ const sectionGap = DEFAULT_STRIP_SECTION_GAP;
5181
+ const layouts = safeSegments.map((segment, index) => buildStripSegmentLayout(segment, {
5182
+ valueMaxWidth: overrides?.valueMaxWidths?.[index]
5183
+ }));
5184
+ const totalWidth = layouts.reduce((sum, segment) => sum + segment.segmentWidth, 0) + Math.max(0, layouts.length - 1) * sectionGap;
5185
+ const rowHeight = layouts.reduce((max, segment) => Math.max(max, segment.rowHeight || 0), 0);
5186
+ return {
5187
+ sectionGap,
5188
+ layouts,
5189
+ totalWidth,
5190
+ rowHeight
5082
5191
  };
5083
- const maxValueWidths = {
5084
- time: 290,
5085
- location: 220,
5086
- ip: 160
5192
+ };
5193
+ var fitTextWithEllipsis = (chars, maxWidth, fontSize) => {
5194
+ const safeChars = Array.isArray(chars) ? chars.slice() : Array.from(String(chars || ""));
5195
+ if (!safeChars.length) {
5196
+ return "-";
5197
+ }
5198
+ if (estimateTextWidth(safeChars.join(""), fontSize) <= maxWidth) {
5199
+ return safeChars.join("").trim() || "-";
5200
+ }
5201
+ while (safeChars.length > 1 && estimateTextWidth(`${safeChars.join("").trimEnd()}\u2026`, fontSize) > maxWidth) {
5202
+ safeChars.pop();
5203
+ }
5204
+ return `${safeChars.join("").trimEnd()}\u2026` || "\u2026";
5205
+ };
5206
+ var splitTextToLines = (value, maxWidth, fontSize, maxLines = 2) => {
5207
+ const chars = Array.from(normalizeWhitespace(value) || "-");
5208
+ const safeMaxWidth = Math.max(18, maxWidth - DEFAULT_STRIP_TEXT_WIDTH_SAFETY);
5209
+ if (!chars.length) {
5210
+ return { lines: ["-"], truncated: false };
5211
+ }
5212
+ const lines = [];
5213
+ let cursor = 0;
5214
+ let truncated = false;
5215
+ while (cursor < chars.length && lines.length < maxLines) {
5216
+ while (cursor < chars.length && chars[cursor] === " ") {
5217
+ cursor += 1;
5218
+ }
5219
+ if (cursor >= chars.length) {
5220
+ break;
5221
+ }
5222
+ const isLastLine = lines.length === maxLines - 1;
5223
+ const lineChars = [];
5224
+ while (cursor < chars.length) {
5225
+ const nextChars = [...lineChars, chars[cursor]];
5226
+ const remaining = cursor < chars.length - 1;
5227
+ const previewText = nextChars.join("") + (isLastLine && remaining ? "\u2026" : "");
5228
+ if (estimateTextWidth(previewText, fontSize) <= safeMaxWidth || lineChars.length === 0) {
5229
+ lineChars.push(chars[cursor]);
5230
+ cursor += 1;
5231
+ continue;
5232
+ }
5233
+ break;
5234
+ }
5235
+ while (cursor < chars.length && chars[cursor] === " ") {
5236
+ cursor += 1;
5237
+ }
5238
+ if (isLastLine && cursor < chars.length) {
5239
+ lines.push(fitTextWithEllipsis([...lineChars, ...chars.slice(cursor)], safeMaxWidth, fontSize));
5240
+ truncated = true;
5241
+ cursor = chars.length;
5242
+ continue;
5243
+ }
5244
+ lines.push(lineChars.join("").trim() || "-");
5245
+ }
5246
+ return {
5247
+ lines: lines.length > 0 ? lines : ["-"],
5248
+ truncated
5087
5249
  };
5088
- const promptSegment = safeSegments.find((segment) => segment?.kind === "prompt") || null;
5089
- const fixedSegments = safeSegments.filter((segment) => segment?.kind !== "prompt");
5090
- const fixedLayouts = fixedSegments.map((segment) => {
5091
- const fontSize = otherFontSizes[segment.kind] || 17;
5092
- const maxValueWidth = maxValueWidths[segment.kind] || 220;
5093
- const renderValue = fitTextToWidth(segment.rawValue || segment.value || "-", maxValueWidth, fontSize);
5094
- const valueWidth = Math.min(maxValueWidth, estimateTextWidth(renderValue, fontSize) + 8);
5095
- return {
5096
- ...segment,
5097
- fontSize,
5098
- labelWidth,
5099
- renderValue,
5100
- segmentWidth: labelWidth + valueWidth
5101
- };
5250
+ };
5251
+ var resolveMetadataMinValueWidth = (segment) => {
5252
+ switch (segment?.kind) {
5253
+ case "time":
5254
+ return Math.max(148, estimateTextWidth("2026-04-23 21:27:16 UTC+8", DEFAULT_STRIP_VALUE_FONT_SIZE) + 8);
5255
+ case "ip":
5256
+ return Math.max(108, estimateTextWidth("255.255.255.255", DEFAULT_STRIP_VALUE_FONT_SIZE) + 8);
5257
+ case "location":
5258
+ return 76;
5259
+ default:
5260
+ return 56;
5261
+ }
5262
+ };
5263
+ var buildMetadataRowLayout = (segments, contentWidth) => {
5264
+ const safeSegments = Array.isArray(segments) ? segments.filter(Boolean) : [];
5265
+ const naturalRow = buildStripRowLayout(safeSegments);
5266
+ if (!safeSegments.length || naturalRow.totalWidth <= contentWidth) {
5267
+ return naturalRow;
5268
+ }
5269
+ const sectionGap = naturalRow.sectionGap;
5270
+ const totalLabelWidth = naturalRow.layouts.reduce((sum, segment) => sum + segment.labelWidth, 0);
5271
+ const availableValueWidth = Math.max(
5272
+ 72,
5273
+ Math.floor(contentWidth - totalLabelWidth - Math.max(0, safeSegments.length - 1) * sectionGap)
5274
+ );
5275
+ const minValueWidths = naturalRow.layouts.map((segment) => resolveMetadataMinValueWidth(segment));
5276
+ const naturalValueWidths = naturalRow.layouts.map((segment) => segment.valueWidth);
5277
+ const valueWidths = naturalValueWidths.slice();
5278
+ let overflow = naturalRow.totalWidth - contentWidth;
5279
+ const shrinkOrder = ["location", "ip", "time"];
5280
+ shrinkOrder.forEach((kind) => {
5281
+ if (overflow <= 0) return;
5282
+ naturalRow.layouts.forEach((segment, index) => {
5283
+ if (overflow <= 0 || segment.kind !== kind) return;
5284
+ const minWidth = minValueWidths[index];
5285
+ const reducible = Math.max(0, valueWidths[index] - minWidth);
5286
+ if (!reducible) return;
5287
+ const reduction = Math.min(reducible, overflow);
5288
+ valueWidths[index] -= reduction;
5289
+ overflow -= reduction;
5290
+ });
5102
5291
  });
5103
- const fixedWidth = fixedLayouts.reduce((sum, segment) => sum + segment.segmentWidth, 0);
5104
- const totalGapWidth = Math.max(0, safeSegments.length - 1) * sectionGap;
5105
- const promptValueWidth = Math.max(
5106
- 180,
5107
- Math.floor(contentWidth - fixedWidth - totalGapWidth - labelWidth)
5292
+ if (overflow > 0) {
5293
+ const minTotalWidth = minValueWidths.reduce((sum, width) => sum + width, 0);
5294
+ if (minTotalWidth > availableValueWidth) {
5295
+ const flexibleIndexes = naturalRow.layouts.map((segment, index) => ({ segment, index })).filter(({ segment }) => segment.kind !== "time");
5296
+ let extraOverflow = minTotalWidth - availableValueWidth;
5297
+ flexibleIndexes.forEach(({ index }) => {
5298
+ if (extraOverflow <= 0) return;
5299
+ const minFloor = 40;
5300
+ const reducible = Math.max(0, minValueWidths[index] - minFloor);
5301
+ if (!reducible) return;
5302
+ const reduction = Math.min(reducible, extraOverflow);
5303
+ valueWidths[index] -= reduction;
5304
+ extraOverflow -= reduction;
5305
+ });
5306
+ }
5307
+ }
5308
+ return buildStripRowLayout(safeSegments, {
5309
+ valueMaxWidths: valueWidths
5310
+ });
5311
+ };
5312
+ var buildPromptWrapLayout = (segment, contentWidth) => {
5313
+ const label = normalizeWhitespace(segment?.label || "Prompt") || "Prompt";
5314
+ const rawValue = normalizeWhitespace(segment?.rawValue || segment?.value || "-") || "-";
5315
+ const labelFontSize = DEFAULT_STRIP_LABEL_FONT_SIZE;
5316
+ const valueFontSize = DEFAULT_STRIP_VALUE_FONT_SIZE;
5317
+ const labelWidth = Math.max(
5318
+ DEFAULT_STRIP_LABEL_WIDTH,
5319
+ estimateTextWidth(label, labelFontSize) + DEFAULT_STRIP_LABEL_PADDING
5320
+ );
5321
+ const safeTotalWidth = Math.max(labelWidth + 56, Number(contentWidth) || 0);
5322
+ const valueWidth = Math.max(
5323
+ 56,
5324
+ safeTotalWidth - labelWidth - DEFAULT_STRIP_CONTENT_SAFE_PADDING_X
5108
5325
  );
5109
- const promptLayout = promptSegment ? {
5110
- ...promptSegment,
5111
- fontSize: promptFontSize,
5326
+ const result = splitTextToLines(rawValue, valueWidth, valueFontSize, DEFAULT_STRIP_PROMPT_MAX_LINES);
5327
+ const lineHeight = Math.max(18, Math.round(valueFontSize * DEFAULT_STRIP_LINE_HEIGHT_RATIO));
5328
+ const lineWidths = result.lines.map((line) => estimateTextWidth(line, valueFontSize) + DEFAULT_STRIP_VALUE_PADDING);
5329
+ const measuredValueWidth = Math.min(
5330
+ valueWidth,
5331
+ Math.max(56, ...lineWidths)
5332
+ );
5333
+ const rowHeight = Math.round(Math.max(labelFontSize, valueFontSize) * 1.42);
5334
+ return {
5335
+ mode: result.lines.length === 1 ? "single" : "wrapped",
5336
+ label,
5337
+ rawValue,
5338
+ labelFontSize,
5112
5339
  labelWidth,
5113
- renderValue: fitTextToWidth(promptSegment.rawValue || promptSegment.value || "-", promptValueWidth, promptFontSize),
5114
- segmentWidth: labelWidth + promptValueWidth
5115
- } : null;
5116
- return [
5117
- ...promptLayout ? [promptLayout] : [],
5118
- ...fixedLayouts
5119
- ];
5340
+ valueFontSize,
5341
+ valueWidth,
5342
+ lines: result.lines,
5343
+ truncated: result.truncated,
5344
+ lineHeight,
5345
+ blockHeight: result.lines.length === 1 ? rowHeight : Math.max(lineHeight, result.lines.length * lineHeight),
5346
+ totalWidth: labelWidth + measuredValueWidth
5347
+ };
5348
+ };
5349
+ var buildStripLayout = (segments, contentWidth) => {
5350
+ const safeSegments = Array.isArray(segments) ? segments.filter(Boolean) : [];
5351
+ const promptSegment = safeSegments.find((segment) => segment.kind === "prompt") || safeSegments[0] || null;
5352
+ const metadataSegments = safeSegments.filter((segment) => segment !== promptSegment);
5353
+ const zoneGap = DEFAULT_STRIP_ZONE_GAP;
5354
+ let metadataRow = buildMetadataRowLayout(
5355
+ metadataSegments,
5356
+ Math.max(220, contentWidth)
5357
+ );
5358
+ let metadataStartX = Math.max(0, contentWidth - (metadataRow?.totalWidth || 0));
5359
+ let promptAvailableWidth = Math.max(
5360
+ 220,
5361
+ metadataStartX - zoneGap - DEFAULT_STRIP_PROMPT_TRAILING_PADDING
5362
+ );
5363
+ if (promptAvailableWidth < DEFAULT_STRIP_PROMPT_MIN_TOTAL_WIDTH && metadataSegments.length > 0) {
5364
+ const tighterMetadataWidth = Math.max(180, contentWidth - DEFAULT_STRIP_PROMPT_MIN_TOTAL_WIDTH - zoneGap);
5365
+ metadataRow = buildMetadataRowLayout(metadataSegments, tighterMetadataWidth);
5366
+ metadataStartX = Math.max(0, contentWidth - (metadataRow?.totalWidth || 0));
5367
+ promptAvailableWidth = Math.max(
5368
+ 220,
5369
+ metadataStartX - zoneGap - DEFAULT_STRIP_PROMPT_TRAILING_PADDING
5370
+ );
5371
+ }
5372
+ const preferredPromptWidth = Math.min(
5373
+ promptAvailableWidth,
5374
+ Math.max(280, Math.round(contentWidth * DEFAULT_STRIP_PROMPT_PREFERRED_WIDTH_RATIO))
5375
+ );
5376
+ let promptRow = buildPromptWrapLayout(promptSegment, preferredPromptWidth);
5377
+ if (promptRow?.truncated && promptAvailableWidth > preferredPromptWidth + 24) {
5378
+ promptRow = buildPromptWrapLayout(promptSegment, promptAvailableWidth);
5379
+ }
5380
+ const contentHeight = Math.max(
5381
+ promptRow?.blockHeight || 0,
5382
+ metadataRow?.rowHeight || 0,
5383
+ 24
5384
+ );
5385
+ const verticalPadding = contentHeight > (metadataRow?.rowHeight || 0) ? 18 : 16;
5386
+ const stripHeight = Math.max(
5387
+ contentHeight > (metadataRow?.rowHeight || 0) ? DEFAULT_STRIP_WRAPPED_MIN_HEIGHT : DEFAULT_STRIP_ONE_LINE_HEIGHT,
5388
+ Math.ceil(contentHeight + verticalPadding * 2)
5389
+ );
5390
+ return {
5391
+ mode: "zoned",
5392
+ height: stripHeight,
5393
+ contentHeight,
5394
+ promptRow,
5395
+ promptStartX: 0,
5396
+ metadataRow,
5397
+ metadataStartX
5398
+ };
5399
+ };
5400
+ var renderStripRow = (row, options = {}) => {
5401
+ const startX = Number(options.startX) || 0;
5402
+ const centerY = Number(options.centerY) || 0;
5403
+ const fontFamily = String(options.fontFamily || "");
5404
+ const showDividers = options.showDividers !== false;
5405
+ const layouts = Array.isArray(row?.layouts) ? row.layouts : [];
5406
+ if (!layouts.length) {
5407
+ return "";
5408
+ }
5409
+ const rowHeight = Math.max(18, Number(row?.rowHeight) || 18);
5410
+ const dividerHalfHeight = Math.max(12, Math.round(rowHeight * 0.56));
5411
+ const sectionGap = Math.max(0, Number(row?.sectionGap) || 0);
5412
+ const svgSegments = [];
5413
+ let currentX = startX;
5414
+ layouts.forEach((segment, index) => {
5415
+ const label = escapeXml(segment.label || "");
5416
+ const value = escapeXml(segment.renderValue || "-");
5417
+ const labelX = currentX;
5418
+ const valueX = currentX + segment.labelWidth;
5419
+ svgSegments.push(`
5420
+ <text
5421
+ x="${labelX}"
5422
+ y="${centerY}"
5423
+ fill="#111111"
5424
+ fill-opacity="0.54"
5425
+ font-family="${fontFamily}"
5426
+ font-size="${segment.labelFontSize}"
5427
+ font-weight="650"
5428
+ dominant-baseline="middle"
5429
+ >${label}</text>
5430
+ <text
5431
+ x="${valueX}"
5432
+ y="${centerY}"
5433
+ fill="#111111"
5434
+ font-family="${fontFamily}"
5435
+ font-size="${segment.valueFontSize}"
5436
+ font-weight="${segment.kind === "prompt" ? 760 : 710}"
5437
+ dominant-baseline="middle"
5438
+ >${value}</text>
5439
+ `);
5440
+ currentX += segment.segmentWidth;
5441
+ if (showDividers && index < layouts.length - 1) {
5442
+ const lineX = currentX + Math.round(sectionGap / 2);
5443
+ svgSegments.push(`
5444
+ <line
5445
+ x1="${lineX}"
5446
+ y1="${centerY - dividerHalfHeight}"
5447
+ x2="${lineX}"
5448
+ y2="${centerY + dividerHalfHeight}"
5449
+ stroke="#94a3b8"
5450
+ stroke-opacity="0.34"
5451
+ stroke-width="1"
5452
+ />
5453
+ `);
5454
+ currentX += sectionGap;
5455
+ }
5456
+ });
5457
+ return svgSegments.join("");
5458
+ };
5459
+ var renderStripDivider = (x, centerY, height) => {
5460
+ const safeX = Number(x) || 0;
5461
+ const safeCenterY = Number(centerY) || 0;
5462
+ const safeHeight = Math.max(18, Number(height) || 18);
5463
+ const halfHeight = Math.max(12, Math.round(safeHeight * 0.56));
5464
+ return `
5465
+ <line
5466
+ x1="${safeX}"
5467
+ y1="${safeCenterY - halfHeight}"
5468
+ x2="${safeX}"
5469
+ y2="${safeCenterY + halfHeight}"
5470
+ stroke="#94a3b8"
5471
+ stroke-opacity="0.34"
5472
+ stroke-width="1"
5473
+ />
5474
+ `;
5120
5475
  };
5121
5476
  var buildWatermarkifySvg = (meta, imageWidth, imageHeight) => {
5122
5477
  const hasWatermark = meta?.watermark?.enabled !== false && normalizeText(meta?.watermarkText);
@@ -5166,67 +5521,97 @@ var buildWatermarkifySvg = (meta, imageWidth, imageHeight) => {
5166
5521
  parts.push(`<g id="watermarks">${stamps.join("")}</g>`);
5167
5522
  }
5168
5523
  if (hasStrip) {
5169
- const stripHeight = DEFAULT_STRIP_HEIGHT;
5170
- const stripY = height - stripHeight;
5171
5524
  const logoSize = 28;
5172
5525
  const logoX = DEFAULT_STRIP_PADDING_LEFT;
5173
- const logoY = stripY + Math.round((stripHeight - logoSize) / 2);
5174
5526
  const contentStartX = logoX + logoSize + DEFAULT_STRIP_GAP;
5175
5527
  const contentWidth = width - contentStartX - DEFAULT_STRIP_PADDING_RIGHT;
5528
+ const stripLayout = buildStripLayout(meta.stripSegments, contentWidth);
5529
+ const stripHeight = stripLayout.height;
5530
+ const stripY = height - stripHeight;
5531
+ const logoY = Number((stripY + (stripHeight - logoSize) / 2).toFixed(2));
5532
+ let stripContentSvg = "";
5176
5533
  const centerY = stripY + stripHeight / 2 + 1;
5177
- const layouts = buildStripSegmentLayouts(meta.stripSegments, contentWidth);
5178
- const svgSegments = [];
5179
- let currentX = contentStartX;
5180
- layouts.forEach((segment, index) => {
5181
- const label = escapeXml(segment.label || "");
5182
- const value = escapeXml(segment.renderValue || "-");
5183
- const labelX = currentX;
5184
- const valueX = currentX + segment.labelWidth;
5185
- svgSegments.push(`
5534
+ const promptRow = stripLayout.promptRow;
5535
+ const metadataRow = stripLayout.metadataRow;
5536
+ const promptParts = [];
5537
+ const hasPrompt = Boolean(promptRow?.label);
5538
+ const hasMetadata = Array.isArray(metadataRow?.layouts) && metadataRow.layouts.length > 0;
5539
+ const zoneDividerX = hasPrompt && hasMetadata ? contentStartX + stripLayout.metadataStartX - DEFAULT_STRIP_ZONE_GAP / 2 : null;
5540
+ if (promptRow?.mode === "single") {
5541
+ promptParts.push(renderStripRow({
5542
+ layouts: [
5543
+ {
5544
+ kind: "prompt",
5545
+ label: promptRow.label,
5546
+ renderValue: promptRow.lines[0] || "-",
5547
+ labelFontSize: promptRow.labelFontSize,
5548
+ valueFontSize: promptRow.valueFontSize,
5549
+ labelWidth: promptRow.labelWidth,
5550
+ segmentWidth: promptRow.labelWidth + promptRow.valueWidth
5551
+ }
5552
+ ],
5553
+ sectionGap: 0,
5554
+ rowHeight: promptRow.blockHeight
5555
+ }, {
5556
+ startX: contentStartX + stripLayout.promptStartX,
5557
+ centerY,
5558
+ fontFamily,
5559
+ showDividers: false
5560
+ }));
5561
+ } else if (promptRow) {
5562
+ const promptStartX = contentStartX + stripLayout.promptStartX;
5563
+ const labelX = promptStartX;
5564
+ const valueX = promptStartX + promptRow.labelWidth;
5565
+ const blockTop = centerY - promptRow.blockHeight / 2;
5566
+ const labelY = centerY;
5567
+ promptParts.push(`
5186
5568
  <text
5187
5569
  x="${labelX}"
5188
- y="${centerY}"
5570
+ y="${labelY}"
5189
5571
  fill="#111111"
5190
- fill-opacity="0.42"
5572
+ fill-opacity="0.54"
5191
5573
  font-family="${fontFamily}"
5192
- font-size="12"
5193
- font-weight="600"
5574
+ font-size="${promptRow.labelFontSize}"
5575
+ font-weight="650"
5194
5576
  dominant-baseline="middle"
5195
- >${label}</text>
5196
- <text
5197
- x="${valueX}"
5198
- y="${centerY}"
5199
- fill="#111111"
5200
- font-family="${fontFamily}"
5201
- font-size="${segment.fontSize}"
5202
- font-weight="${segment.kind === "prompt" ? 760 : 710}"
5203
- dominant-baseline="middle"
5204
- >${value}</text>
5577
+ >${escapeXml(promptRow.label)}</text>
5205
5578
  `);
5206
- currentX += segment.segmentWidth;
5207
- if (index < layouts.length - 1) {
5208
- const lineX = currentX + 8;
5209
- svgSegments.push(`
5210
- <line
5211
- x1="${lineX}"
5212
- y1="${stripY + 26}"
5213
- x2="${lineX}"
5214
- y2="${stripY + stripHeight - 26}"
5215
- stroke="#94a3b8"
5216
- stroke-opacity="0.34"
5217
- stroke-width="1"
5218
- />
5579
+ promptRow.lines.forEach((line, index) => {
5580
+ const lineY = blockTop + index * promptRow.lineHeight + promptRow.lineHeight / 2;
5581
+ promptParts.push(`
5582
+ <text
5583
+ x="${valueX}"
5584
+ y="${lineY}"
5585
+ fill="#111111"
5586
+ font-family="${fontFamily}"
5587
+ font-size="${promptRow.valueFontSize}"
5588
+ font-weight="760"
5589
+ dominant-baseline="middle"
5590
+ >${escapeXml(line)}</text>
5219
5591
  `);
5220
- currentX += DEFAULT_STRIP_GAP + 16;
5221
- }
5222
- });
5592
+ });
5593
+ }
5594
+ stripContentSvg = [
5595
+ promptParts.join(""),
5596
+ zoneDividerX == null ? "" : renderStripDivider(
5597
+ zoneDividerX,
5598
+ centerY,
5599
+ Math.max(promptRow?.blockHeight || 0, metadataRow?.rowHeight || 0)
5600
+ ),
5601
+ renderStripRow(metadataRow, {
5602
+ startX: contentStartX + stripLayout.metadataStartX,
5603
+ centerY,
5604
+ fontFamily,
5605
+ showDividers: true
5606
+ })
5607
+ ].join("");
5223
5608
  parts.push(`
5224
5609
  <g id="strip">
5225
5610
  <rect x="0" y="${stripY}" width="${width}" height="${stripHeight}" fill="#ffffff" fill-opacity="0.985" />
5226
5611
  <rect x="0" y="${stripY}" width="${width}" height="1" fill="#111111" fill-opacity="0.10" />
5227
5612
  <rect x="${DEFAULT_STRIP_PADDING_LEFT}" y="${stripY}" width="${Math.max(120, Math.round(width * 0.42))}" height="2" fill="url(#stripAccent)" />
5228
5613
  ${meta.stripLogoSrc ? `<image href="${escapeXml(meta.stripLogoSrc)}" x="${logoX}" y="${logoY}" width="${logoSize}" height="${logoSize}" />` : ""}
5229
- ${svgSegments.join("")}
5614
+ ${stripContentSvg}
5230
5615
  </g>
5231
5616
  `);
5232
5617
  }
@@ -5342,6 +5727,20 @@ var normalizeShare = (share) => {
5342
5727
  xurl: normalizeXurl(source.xurl)
5343
5728
  };
5344
5729
  };
5730
+ var resolveCaptureScreenWatermarkify = (page, optionValue) => {
5731
+ if (optionValue === false) {
5732
+ return normalizeScreenshotWatermarkify(false);
5733
+ }
5734
+ const runtimeQuery = String(page?.[PageRuntimeStateKey]?.query || "").trim();
5735
+ if (optionValue == null || optionValue === true) {
5736
+ return normalizeScreenshotWatermarkify(runtimeQuery ? { query: runtimeQuery } : true);
5737
+ }
5738
+ const optionSource = optionValue && typeof optionValue === "object" ? optionValue : {};
5739
+ return normalizeScreenshotWatermarkify({
5740
+ ...runtimeQuery ? { query: runtimeQuery } : {},
5741
+ ...optionSource
5742
+ });
5743
+ };
5345
5744
  var getByPathSegments = (source, pathSegments) => {
5346
5745
  if (!Array.isArray(pathSegments) || pathSegments.length === 0) return void 0;
5347
5746
  let current = source;
@@ -5665,12 +6064,12 @@ var Share = {
5665
6064
  * @returns {Promise<string>} base64 png
5666
6065
  */
5667
6066
  async captureScreen(page, options = {}) {
5668
- const originalViewport = page.viewportSize();
5669
- const defaultBuffer = Math.round((originalViewport?.height || 1080) / 2);
6067
+ const originalViewport = await resolveCurrentViewportSize(page);
6068
+ const defaultBuffer = Math.round((originalViewport.height || 1080) / 2);
5670
6069
  const buffer = options.buffer ?? defaultBuffer;
5671
6070
  const restore = options.restore ?? false;
5672
6071
  const maxHeight = options.maxHeight ?? 8e3;
5673
- const screenshotWatermarkify = normalizeScreenshotWatermarkify(options.watermarkify ?? true);
6072
+ const screenshotWatermarkify = resolveCaptureScreenWatermarkify(page, options.watermarkify);
5674
6073
  try {
5675
6074
  const maxScrollHeight = await page.evaluate(() => {
5676
6075
  let maxHeight2 = document.body.scrollHeight;
@@ -5694,7 +6093,7 @@ var Share = {
5694
6093
  });
5695
6094
  const targetHeight = Math.min(maxScrollHeight + buffer, maxHeight);
5696
6095
  await page.setViewportSize({
5697
- width: originalViewport?.width || 1280,
6096
+ width: originalViewport.width,
5698
6097
  height: targetHeight
5699
6098
  });
5700
6099
  await delay2(1e3);
@@ -5726,9 +6125,7 @@ var Share = {
5726
6125
  el.classList.remove("__pk_expanded__");
5727
6126
  });
5728
6127
  });
5729
- if (originalViewport) {
5730
- await page.setViewportSize(originalViewport);
5731
- }
6128
+ await page.setViewportSize(originalViewport);
5732
6129
  }
5733
6130
  }
5734
6131
  }