@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.cjs CHANGED
@@ -435,6 +435,33 @@ function createInternalLogger(moduleName, explicitLogger) {
435
435
  };
436
436
  }
437
437
 
438
+ // src/internals/viewport.js
439
+ var toPositiveInt = (value) => {
440
+ const number = Math.round(Number(value) || 0);
441
+ return number > 0 ? number : 0;
442
+ };
443
+ var resolveCurrentViewportSize = async (page, fallback = {}) => {
444
+ const directViewport = page?.viewportSize?.();
445
+ const directWidth = toPositiveInt(directViewport?.width);
446
+ const directHeight = toPositiveInt(directViewport?.height);
447
+ if (directWidth && directHeight) {
448
+ return {
449
+ width: directWidth,
450
+ height: directHeight
451
+ };
452
+ }
453
+ const measuredViewport = await page?.evaluate?.(() => ({
454
+ width: window.innerWidth || document.documentElement?.clientWidth || document.body?.clientWidth || 0,
455
+ height: window.innerHeight || document.documentElement?.clientHeight || document.body?.clientHeight || 0
456
+ })).catch(() => null);
457
+ const fallbackWidth = toPositiveInt(fallback?.width);
458
+ const fallbackHeight = toPositiveInt(fallback?.height);
459
+ return {
460
+ width: toPositiveInt(measuredViewport?.width) || fallbackWidth || 1,
461
+ height: toPositiveInt(measuredViewport?.height) || fallbackHeight || 1
462
+ };
463
+ };
464
+
438
465
  // src/internals/screenshot.js
439
466
  var logger = createInternalLogger("Screenshot");
440
467
  var DEFAULT_TIMEOUT_MS = 5e3;
@@ -461,7 +488,7 @@ var normalizeQuality = (value, type) => {
461
488
  };
462
489
  var buildFullPageClip = (metrics, viewport, maxClipHeight) => {
463
490
  const contentSize = metrics && typeof metrics === "object" ? metrics.contentSize || null : null;
464
- const width = Math.max(1, Math.ceil(contentSize?.width || viewport.width || 1));
491
+ const width = Math.max(1, Math.ceil(viewport.width || contentSize?.width || 1));
465
492
  let height = Math.max(1, Math.ceil(contentSize?.height || viewport.height || 1));
466
493
  if (maxClipHeight > 0) {
467
494
  height = Math.min(height, maxClipHeight);
@@ -498,7 +525,7 @@ var capturePageScreenshot = async (page, options = {}) => {
498
525
  const session = await context.newCDPSession(page);
499
526
  try {
500
527
  const metrics = await session.send("Page.getLayoutMetrics");
501
- const viewport = page.viewportSize() || { width: 1280, height: 720 };
528
+ const viewport = await resolveCurrentViewportSize(page);
502
529
  const captureParams = {
503
530
  format: type,
504
531
  fromSurface: true,
@@ -952,6 +979,9 @@ var ProxyMeterRuntime = {
952
979
  getProxyMeterSnapshot
953
980
  };
954
981
 
982
+ // src/internals/constants.js
983
+ var PageRuntimeStateKey = "__playwright_toolkit_runtime_state__";
984
+
955
985
  // src/runtime-env.js
956
986
  var BROWSER_PROFILE_SCHEMA_VERSION = 1;
957
987
  var rememberedRuntimeState = null;
@@ -1458,6 +1488,7 @@ var RuntimeEnv = {
1458
1488
  parseInput(input = {}, actor = "") {
1459
1489
  const runtime2 = tryParseJSON(input?.runtime) || {};
1460
1490
  const resolvedActor = String(actor || input?.actor || "").trim();
1491
+ const query = String(input?.query || "").trim();
1461
1492
  const cookies = normalizeCookies(runtime2?.cookies);
1462
1493
  const cookieMap = buildCookieMap(cookies);
1463
1494
  const localStorage = normalizeLocalStorage(runtime2?.local_storage);
@@ -1481,6 +1512,7 @@ var RuntimeEnv = {
1481
1512
  actor: resolvedActor,
1482
1513
  runtime: normalizedRuntime,
1483
1514
  envId,
1515
+ query,
1484
1516
  auth,
1485
1517
  cookies,
1486
1518
  cookieMap,
@@ -1548,6 +1580,12 @@ var RuntimeEnv = {
1548
1580
  async applyToPage(page, source = {}, options = {}) {
1549
1581
  if (!page) return;
1550
1582
  const state = normalizeRuntimeState(source, options?.actor || "");
1583
+ Object.defineProperty(page, PageRuntimeStateKey, {
1584
+ configurable: true,
1585
+ enumerable: false,
1586
+ writable: true,
1587
+ value: state
1588
+ });
1551
1589
  const localStorage = state.localStorage || {};
1552
1590
  const sessionStorage = state.sessionStorage || {};
1553
1591
  const cookies = (state.cookies || []).map((cookie) => {
@@ -4560,11 +4598,26 @@ var DEFAULT_WATERMARK_CELL_HEIGHT = 330;
4560
4598
  var DEFAULT_STRIP_LOGO_URL = "https://static.heartbitai.com/geo/icon/favicon.png";
4561
4599
  var DEFAULT_IP_LOOKUP_URL = "http://myip.ipip.net";
4562
4600
  var DEFAULT_LOGO_FETCH_TIMEOUT_MS = 2500;
4563
- var DEFAULT_STRIP_HEIGHT = 78;
4564
- var DEFAULT_STRIP_PADDING_LEFT = 20;
4565
- var DEFAULT_STRIP_PADDING_RIGHT = 22;
4566
- var DEFAULT_STRIP_GAP = 16;
4601
+ var DEFAULT_STRIP_ONE_LINE_HEIGHT = 78;
4602
+ var DEFAULT_STRIP_WRAPPED_MIN_HEIGHT = 108;
4603
+ var DEFAULT_STRIP_PADDING_LEFT = 22;
4604
+ var DEFAULT_STRIP_PADDING_RIGHT = 28;
4605
+ var DEFAULT_STRIP_GAP = 18;
4567
4606
  var DEFAULT_STRIP_LABEL_WIDTH = 56;
4607
+ var DEFAULT_STRIP_PROMPT_MAX_SAFE_CHARS = 180;
4608
+ var DEFAULT_STRIP_PROMPT_MIN_TOTAL_WIDTH = 320;
4609
+ var DEFAULT_STRIP_PROMPT_PREFERRED_WIDTH_RATIO = 0.58;
4610
+ var DEFAULT_STRIP_ZONE_GAP = 18;
4611
+ var DEFAULT_STRIP_PROMPT_TRAILING_PADDING = 16;
4612
+ var DEFAULT_STRIP_LABEL_FONT_SIZE = 14.5;
4613
+ var DEFAULT_STRIP_VALUE_FONT_SIZE = 15.5;
4614
+ var DEFAULT_STRIP_VALUE_PADDING = 10;
4615
+ var DEFAULT_STRIP_LABEL_PADDING = 14;
4616
+ var DEFAULT_STRIP_SECTION_GAP = DEFAULT_STRIP_GAP + 16;
4617
+ var DEFAULT_STRIP_LINE_HEIGHT_RATIO = 1.28;
4618
+ var DEFAULT_STRIP_PROMPT_MAX_LINES = 3;
4619
+ var DEFAULT_STRIP_CONTENT_SAFE_PADDING_X = 12;
4620
+ var DEFAULT_STRIP_TEXT_WIDTH_SAFETY = 10;
4568
4621
  var WEAK_LOCATION_VALUES = /* @__PURE__ */ new Set(["cn", "\u4E2D\u56FD"]);
4569
4622
  var LOCATION_NETWORK_SUFFIX_PATTERNS = [
4570
4623
  /(?:中国)?移动$/i,
@@ -4589,6 +4642,7 @@ var LOCATION_NETWORK_SUFFIX_PATTERNS = [
4589
4642
  /digitalocean$/i
4590
4643
  ];
4591
4644
  var cachedStripLogoSrcPromise = null;
4645
+ var cachedEnrichmentByContext = /* @__PURE__ */ new WeakMap();
4592
4646
  var normalizeText = (value) => String(value || "").trim();
4593
4647
  var toInline = (value, maxLen = 200) => {
4594
4648
  const text = normalizeText(value);
@@ -4600,6 +4654,15 @@ var shortenTail = (value, maxLen = 80) => {
4600
4654
  if (!text || text.length <= maxLen) return text;
4601
4655
  return `${text.slice(0, Math.max(0, maxLen - 1)).trimEnd()}\u2026`;
4602
4656
  };
4657
+ var shortenByCharacters = (value, maxChars = 80) => {
4658
+ const text = normalizeWhitespace(value);
4659
+ if (!text) return "";
4660
+ const chars = Array.from(text);
4661
+ if (chars.length <= maxChars) {
4662
+ return text;
4663
+ }
4664
+ return `${chars.slice(0, Math.max(0, maxChars - 1)).join("").trimEnd()}\u2026`;
4665
+ };
4603
4666
  var padDatePart = (value) => String(value).padStart(2, "0");
4604
4667
  var formatUtcOffsetLabel = (offsetHours = DEFAULT_TIMEZONE_OFFSET) => {
4605
4668
  const sign = offsetHours >= 0 ? "+" : "-";
@@ -4672,6 +4735,29 @@ var fillEnrichment = (target, source) => {
4672
4735
  }
4673
4736
  return target;
4674
4737
  };
4738
+ var readCachedEnrichment = (page) => {
4739
+ const context = page && typeof page.context === "function" ? page.context() : null;
4740
+ if (!context) {
4741
+ return null;
4742
+ }
4743
+ const cached = cachedEnrichmentByContext.get(context);
4744
+ return cached && typeof cached === "object" ? cached : null;
4745
+ };
4746
+ var writeCachedEnrichment = (page, source) => {
4747
+ const context = page && typeof page.context === "function" ? page.context() : null;
4748
+ if (!context || !source || typeof source !== "object") {
4749
+ return;
4750
+ }
4751
+ const current = cachedEnrichmentByContext.get(context) || {};
4752
+ const next = {
4753
+ ip: toInline(source.ip || current.ip, 80),
4754
+ location: toInline(source.location || current.location, 80)
4755
+ };
4756
+ if (!next.ip && !next.location) {
4757
+ return;
4758
+ }
4759
+ cachedEnrichmentByContext.set(context, next);
4760
+ };
4675
4761
  var getHostname = (url) => {
4676
4762
  try {
4677
4763
  const parsed = new URL(url);
@@ -4801,44 +4887,43 @@ var openProbePage = async (page) => {
4801
4887
  if (!context) {
4802
4888
  return null;
4803
4889
  }
4890
+ const browser = typeof context.browser === "function" ? context.browser() : null;
4891
+ if (browser && typeof browser.newContext === "function") {
4892
+ const probeContext = await browser.newContext().catch(() => null);
4893
+ if (probeContext && typeof probeContext.newPage === "function") {
4894
+ const probePage = await probeContext.newPage().catch(async () => {
4895
+ await probeContext.close().catch(() => {
4896
+ });
4897
+ return null;
4898
+ });
4899
+ if (probePage) {
4900
+ return {
4901
+ page: probePage,
4902
+ close: async () => {
4903
+ await probeContext.close().catch(() => {
4904
+ });
4905
+ }
4906
+ };
4907
+ }
4908
+ } else {
4909
+ await probeContext?.close?.().catch(() => {
4910
+ });
4911
+ }
4912
+ }
4804
4913
  if (typeof context.newPage === "function") {
4805
4914
  try {
4806
- const probePage2 = await context.newPage();
4915
+ const probePage = await context.newPage();
4807
4916
  return {
4808
- page: probePage2,
4917
+ page: probePage,
4809
4918
  close: async () => {
4810
- await probePage2.close().catch(() => {
4919
+ await probePage.close().catch(() => {
4811
4920
  });
4812
4921
  }
4813
4922
  };
4814
4923
  } catch {
4815
4924
  }
4816
4925
  }
4817
- const browser = typeof context.browser === "function" ? context.browser() : null;
4818
- if (!browser || typeof browser.newContext !== "function") {
4819
- return null;
4820
- }
4821
- const probeContext = await browser.newContext().catch(() => null);
4822
- if (!probeContext || typeof probeContext.newPage !== "function") {
4823
- await probeContext?.close?.().catch(() => {
4824
- });
4825
- return null;
4826
- }
4827
- const probePage = await probeContext.newPage().catch(async () => {
4828
- await probeContext.close().catch(() => {
4829
- });
4830
- return null;
4831
- });
4832
- if (!probePage) {
4833
- return null;
4834
- }
4835
- return {
4836
- page: probePage,
4837
- close: async () => {
4838
- await probeContext.close().catch(() => {
4839
- });
4840
- }
4841
- };
4926
+ return null;
4842
4927
  };
4843
4928
  var resolveWithIpLookup = async (page, options = {}) => {
4844
4929
  if (!page || typeof page.context !== "function" || options.ipLookup === false) {
@@ -4883,10 +4968,14 @@ var resolveWithIpLookup = async (page, options = {}) => {
4883
4968
  };
4884
4969
  var resolveEnrichment = async (page, baseMeta, options) => {
4885
4970
  const response = options.response && typeof options.response === "object" ? options.response : null;
4971
+ const cached = readCachedEnrichment(page);
4886
4972
  const merged = {
4887
4973
  ip: toInline(options.ip, 80),
4888
4974
  location: toInline(options.location, 80)
4889
4975
  };
4976
+ if (!merged.ip || !merged.location) {
4977
+ fillEnrichment(merged, cached);
4978
+ }
4890
4979
  if (!merged.ip || !merged.location) {
4891
4980
  fillEnrichment(
4892
4981
  merged,
@@ -4910,6 +4999,7 @@ var resolveEnrichment = async (page, baseMeta, options) => {
4910
4999
  merged.location = headerLocation || merged.location;
4911
5000
  }
4912
5001
  }
5002
+ writeCachedEnrichment(page, merged);
4913
5003
  return merged;
4914
5004
  };
4915
5005
  var buildWatermarkStamp = ({ prompt, captureTime, ip, location }) => {
@@ -4922,7 +5012,12 @@ var buildWatermarkStamp = ({ prompt, captureTime, ip, location }) => {
4922
5012
  return parts.join(" | ");
4923
5013
  };
4924
5014
  var buildStripSegments = ({ prompt, captureTime, ip, location }) => {
4925
- const promptValue = toInline(prompt, 240) || "-";
5015
+ const promptValue = shortenByCharacters(
5016
+ normalizeWhitespace(prompt) || "-",
5017
+ DEFAULT_STRIP_PROMPT_MAX_SAFE_CHARS
5018
+ ) || "-";
5019
+ const ipValue = normalizeWhitespace(ip) || "-";
5020
+ const locationValue = normalizeWhitespace(location) || "-";
4926
5021
  return [
4927
5022
  {
4928
5023
  kind: "prompt",
@@ -4939,14 +5034,14 @@ var buildStripSegments = ({ prompt, captureTime, ip, location }) => {
4939
5034
  {
4940
5035
  kind: "location",
4941
5036
  label: "Loc",
4942
- value: shortenTail(location, 24) || "-",
4943
- rawValue: toInline(location, 80) || "-"
5037
+ value: locationValue,
5038
+ rawValue: locationValue
4944
5039
  },
4945
5040
  {
4946
5041
  kind: "ip",
4947
5042
  label: "IP",
4948
- value: toInline(ip, 28) || "-",
4949
- rawValue: toInline(ip, 80) || "-"
5043
+ value: ipValue,
5044
+ rawValue: ipValue
4950
5045
  }
4951
5046
  ];
4952
5047
  };
@@ -4967,28 +5062,9 @@ var estimateTextWidth = (value, fontSize = 16) => {
4967
5062
  }
4968
5063
  return Math.ceil(width);
4969
5064
  };
4970
- var fitTextToWidth = (value, maxWidth, fontSize = 16) => {
4971
- const text = normalizeWhitespace(value);
4972
- if (!text) return "-";
4973
- const safeMaxWidth = Math.max(fontSize * 3, Number(maxWidth) || 0);
4974
- if (estimateTextWidth(text, fontSize) <= safeMaxWidth) {
4975
- return text;
4976
- }
4977
- const ellipsis = "\u2026";
4978
- const ellipsisWidth = estimateTextWidth(ellipsis, fontSize);
4979
- let current = "";
4980
- for (const char of text) {
4981
- const next = current + char;
4982
- if (estimateTextWidth(next, fontSize) + ellipsisWidth > safeMaxWidth) {
4983
- break;
4984
- }
4985
- current = next;
4986
- }
4987
- return `${current.trimEnd()}${ellipsis}`;
4988
- };
4989
5065
  var resolvePromptFields = (options = {}, fallbackTitle = "") => {
4990
5066
  const query = normalizeText(options.query);
4991
- const prompt = normalizeText(options.prompt) || query || normalizeText(fallbackTitle) || "\u672A\u63D0\u4F9B Prompt";
5067
+ const prompt = query || normalizeText(options.prompt) || normalizeText(fallbackTitle) || "\u672A\u63D0\u4F9B Prompt";
4992
5068
  return {
4993
5069
  prompt,
4994
5070
  query: query || prompt
@@ -5044,8 +5120,8 @@ var normalizeScreenshotWatermarkify = (value) => {
5044
5120
  response: source.response ?? null,
5045
5121
  ip: normalizeText(source.ip),
5046
5122
  location: normalizeText(source.location),
5047
- prompt: toInline(source.prompt, 220),
5048
- query: toInline(source.query, 220),
5123
+ prompt: normalizeWhitespace(source.prompt),
5124
+ query: normalizeWhitespace(source.query),
5049
5125
  ipLookup: source.ipLookup !== false,
5050
5126
  ipLookupTimeoutMs: Number.isFinite(ipLookupTimeoutMsRaw) ? ipLookupTimeoutMsRaw : DEFAULT_IP_LOOKUP_TIMEOUT_MS,
5051
5127
  resolver: typeof source.resolver === "function" ? source.resolver : null,
@@ -5098,53 +5174,332 @@ var resolveScreenshotWatermarkifyMeta = async (page, options = {}) => {
5098
5174
  };
5099
5175
  };
5100
5176
  var buildFontFamily = () => 'MiSans, "SF Pro Display", "PingFang SC", "Helvetica Neue", Arial, sans-serif';
5101
- var buildStripSegmentLayouts = (segments, contentWidth) => {
5177
+ var buildStripSegmentLayout = (segment, options = {}) => {
5178
+ const label = normalizeWhitespace(segment?.label || "");
5179
+ const rawValue = normalizeWhitespace(segment?.rawValue || segment?.value || "-") || "-";
5180
+ const labelFontSize = DEFAULT_STRIP_LABEL_FONT_SIZE;
5181
+ const valueFontSize = DEFAULT_STRIP_VALUE_FONT_SIZE;
5182
+ const labelWidth = Math.max(
5183
+ DEFAULT_STRIP_LABEL_WIDTH,
5184
+ estimateTextWidth(label, labelFontSize) + DEFAULT_STRIP_LABEL_PADDING
5185
+ );
5186
+ const rawValueWidth = Math.max(
5187
+ 24,
5188
+ estimateTextWidth(rawValue, valueFontSize) + DEFAULT_STRIP_VALUE_PADDING
5189
+ );
5190
+ const maxValueWidth = Math.max(0, Number(options.valueMaxWidth) || 0);
5191
+ const renderValue = maxValueWidth > 0 ? fitTextWithEllipsis(rawValue, Math.max(18, maxValueWidth - DEFAULT_STRIP_VALUE_PADDING), valueFontSize) : rawValue;
5192
+ const valueWidth = maxValueWidth > 0 ? Math.max(24, Math.min(maxValueWidth, estimateTextWidth(renderValue, valueFontSize) + DEFAULT_STRIP_VALUE_PADDING)) : rawValueWidth;
5193
+ return {
5194
+ ...segment,
5195
+ label,
5196
+ rawValue,
5197
+ renderValue,
5198
+ labelFontSize,
5199
+ valueFontSize,
5200
+ labelWidth,
5201
+ valueWidth,
5202
+ segmentWidth: labelWidth + valueWidth,
5203
+ rowHeight: Math.round(Math.max(labelFontSize, valueFontSize) * 1.42)
5204
+ };
5205
+ };
5206
+ var buildStripRowLayout = (segments, overrides = {}) => {
5102
5207
  const safeSegments = Array.isArray(segments) ? segments : [];
5103
- const sectionGap = DEFAULT_STRIP_GAP + 16;
5104
- const labelWidth = DEFAULT_STRIP_LABEL_WIDTH;
5105
- const promptFontSize = 15;
5106
- const otherFontSizes = {
5107
- time: 17,
5108
- location: 17,
5109
- ip: 17
5208
+ const sectionGap = DEFAULT_STRIP_SECTION_GAP;
5209
+ const layouts = safeSegments.map((segment, index) => buildStripSegmentLayout(segment, {
5210
+ valueMaxWidth: overrides?.valueMaxWidths?.[index]
5211
+ }));
5212
+ const totalWidth = layouts.reduce((sum, segment) => sum + segment.segmentWidth, 0) + Math.max(0, layouts.length - 1) * sectionGap;
5213
+ const rowHeight = layouts.reduce((max, segment) => Math.max(max, segment.rowHeight || 0), 0);
5214
+ return {
5215
+ sectionGap,
5216
+ layouts,
5217
+ totalWidth,
5218
+ rowHeight
5110
5219
  };
5111
- const maxValueWidths = {
5112
- time: 290,
5113
- location: 220,
5114
- ip: 160
5220
+ };
5221
+ var fitTextWithEllipsis = (chars, maxWidth, fontSize) => {
5222
+ const safeChars = Array.isArray(chars) ? chars.slice() : Array.from(String(chars || ""));
5223
+ if (!safeChars.length) {
5224
+ return "-";
5225
+ }
5226
+ if (estimateTextWidth(safeChars.join(""), fontSize) <= maxWidth) {
5227
+ return safeChars.join("").trim() || "-";
5228
+ }
5229
+ while (safeChars.length > 1 && estimateTextWidth(`${safeChars.join("").trimEnd()}\u2026`, fontSize) > maxWidth) {
5230
+ safeChars.pop();
5231
+ }
5232
+ return `${safeChars.join("").trimEnd()}\u2026` || "\u2026";
5233
+ };
5234
+ var splitTextToLines = (value, maxWidth, fontSize, maxLines = 2) => {
5235
+ const chars = Array.from(normalizeWhitespace(value) || "-");
5236
+ const safeMaxWidth = Math.max(18, maxWidth - DEFAULT_STRIP_TEXT_WIDTH_SAFETY);
5237
+ if (!chars.length) {
5238
+ return { lines: ["-"], truncated: false };
5239
+ }
5240
+ const lines = [];
5241
+ let cursor = 0;
5242
+ let truncated = false;
5243
+ while (cursor < chars.length && lines.length < maxLines) {
5244
+ while (cursor < chars.length && chars[cursor] === " ") {
5245
+ cursor += 1;
5246
+ }
5247
+ if (cursor >= chars.length) {
5248
+ break;
5249
+ }
5250
+ const isLastLine = lines.length === maxLines - 1;
5251
+ const lineChars = [];
5252
+ while (cursor < chars.length) {
5253
+ const nextChars = [...lineChars, chars[cursor]];
5254
+ const remaining = cursor < chars.length - 1;
5255
+ const previewText = nextChars.join("") + (isLastLine && remaining ? "\u2026" : "");
5256
+ if (estimateTextWidth(previewText, fontSize) <= safeMaxWidth || lineChars.length === 0) {
5257
+ lineChars.push(chars[cursor]);
5258
+ cursor += 1;
5259
+ continue;
5260
+ }
5261
+ break;
5262
+ }
5263
+ while (cursor < chars.length && chars[cursor] === " ") {
5264
+ cursor += 1;
5265
+ }
5266
+ if (isLastLine && cursor < chars.length) {
5267
+ lines.push(fitTextWithEllipsis([...lineChars, ...chars.slice(cursor)], safeMaxWidth, fontSize));
5268
+ truncated = true;
5269
+ cursor = chars.length;
5270
+ continue;
5271
+ }
5272
+ lines.push(lineChars.join("").trim() || "-");
5273
+ }
5274
+ return {
5275
+ lines: lines.length > 0 ? lines : ["-"],
5276
+ truncated
5115
5277
  };
5116
- const promptSegment = safeSegments.find((segment) => segment?.kind === "prompt") || null;
5117
- const fixedSegments = safeSegments.filter((segment) => segment?.kind !== "prompt");
5118
- const fixedLayouts = fixedSegments.map((segment) => {
5119
- const fontSize = otherFontSizes[segment.kind] || 17;
5120
- const maxValueWidth = maxValueWidths[segment.kind] || 220;
5121
- const renderValue = fitTextToWidth(segment.rawValue || segment.value || "-", maxValueWidth, fontSize);
5122
- const valueWidth = Math.min(maxValueWidth, estimateTextWidth(renderValue, fontSize) + 8);
5123
- return {
5124
- ...segment,
5125
- fontSize,
5126
- labelWidth,
5127
- renderValue,
5128
- segmentWidth: labelWidth + valueWidth
5129
- };
5278
+ };
5279
+ var resolveMetadataMinValueWidth = (segment) => {
5280
+ switch (segment?.kind) {
5281
+ case "time":
5282
+ return Math.max(148, estimateTextWidth("2026-04-23 21:27:16 UTC+8", DEFAULT_STRIP_VALUE_FONT_SIZE) + 8);
5283
+ case "ip":
5284
+ return Math.max(108, estimateTextWidth("255.255.255.255", DEFAULT_STRIP_VALUE_FONT_SIZE) + 8);
5285
+ case "location":
5286
+ return 76;
5287
+ default:
5288
+ return 56;
5289
+ }
5290
+ };
5291
+ var buildMetadataRowLayout = (segments, contentWidth) => {
5292
+ const safeSegments = Array.isArray(segments) ? segments.filter(Boolean) : [];
5293
+ const naturalRow = buildStripRowLayout(safeSegments);
5294
+ if (!safeSegments.length || naturalRow.totalWidth <= contentWidth) {
5295
+ return naturalRow;
5296
+ }
5297
+ const sectionGap = naturalRow.sectionGap;
5298
+ const totalLabelWidth = naturalRow.layouts.reduce((sum, segment) => sum + segment.labelWidth, 0);
5299
+ const availableValueWidth = Math.max(
5300
+ 72,
5301
+ Math.floor(contentWidth - totalLabelWidth - Math.max(0, safeSegments.length - 1) * sectionGap)
5302
+ );
5303
+ const minValueWidths = naturalRow.layouts.map((segment) => resolveMetadataMinValueWidth(segment));
5304
+ const naturalValueWidths = naturalRow.layouts.map((segment) => segment.valueWidth);
5305
+ const valueWidths = naturalValueWidths.slice();
5306
+ let overflow = naturalRow.totalWidth - contentWidth;
5307
+ const shrinkOrder = ["location", "ip", "time"];
5308
+ shrinkOrder.forEach((kind) => {
5309
+ if (overflow <= 0) return;
5310
+ naturalRow.layouts.forEach((segment, index) => {
5311
+ if (overflow <= 0 || segment.kind !== kind) return;
5312
+ const minWidth = minValueWidths[index];
5313
+ const reducible = Math.max(0, valueWidths[index] - minWidth);
5314
+ if (!reducible) return;
5315
+ const reduction = Math.min(reducible, overflow);
5316
+ valueWidths[index] -= reduction;
5317
+ overflow -= reduction;
5318
+ });
5130
5319
  });
5131
- const fixedWidth = fixedLayouts.reduce((sum, segment) => sum + segment.segmentWidth, 0);
5132
- const totalGapWidth = Math.max(0, safeSegments.length - 1) * sectionGap;
5133
- const promptValueWidth = Math.max(
5134
- 180,
5135
- Math.floor(contentWidth - fixedWidth - totalGapWidth - labelWidth)
5320
+ if (overflow > 0) {
5321
+ const minTotalWidth = minValueWidths.reduce((sum, width) => sum + width, 0);
5322
+ if (minTotalWidth > availableValueWidth) {
5323
+ const flexibleIndexes = naturalRow.layouts.map((segment, index) => ({ segment, index })).filter(({ segment }) => segment.kind !== "time");
5324
+ let extraOverflow = minTotalWidth - availableValueWidth;
5325
+ flexibleIndexes.forEach(({ index }) => {
5326
+ if (extraOverflow <= 0) return;
5327
+ const minFloor = 40;
5328
+ const reducible = Math.max(0, minValueWidths[index] - minFloor);
5329
+ if (!reducible) return;
5330
+ const reduction = Math.min(reducible, extraOverflow);
5331
+ valueWidths[index] -= reduction;
5332
+ extraOverflow -= reduction;
5333
+ });
5334
+ }
5335
+ }
5336
+ return buildStripRowLayout(safeSegments, {
5337
+ valueMaxWidths: valueWidths
5338
+ });
5339
+ };
5340
+ var buildPromptWrapLayout = (segment, contentWidth) => {
5341
+ const label = normalizeWhitespace(segment?.label || "Prompt") || "Prompt";
5342
+ const rawValue = normalizeWhitespace(segment?.rawValue || segment?.value || "-") || "-";
5343
+ const labelFontSize = DEFAULT_STRIP_LABEL_FONT_SIZE;
5344
+ const valueFontSize = DEFAULT_STRIP_VALUE_FONT_SIZE;
5345
+ const labelWidth = Math.max(
5346
+ DEFAULT_STRIP_LABEL_WIDTH,
5347
+ estimateTextWidth(label, labelFontSize) + DEFAULT_STRIP_LABEL_PADDING
5348
+ );
5349
+ const safeTotalWidth = Math.max(labelWidth + 56, Number(contentWidth) || 0);
5350
+ const valueWidth = Math.max(
5351
+ 56,
5352
+ safeTotalWidth - labelWidth - DEFAULT_STRIP_CONTENT_SAFE_PADDING_X
5136
5353
  );
5137
- const promptLayout = promptSegment ? {
5138
- ...promptSegment,
5139
- fontSize: promptFontSize,
5354
+ const result = splitTextToLines(rawValue, valueWidth, valueFontSize, DEFAULT_STRIP_PROMPT_MAX_LINES);
5355
+ const lineHeight = Math.max(18, Math.round(valueFontSize * DEFAULT_STRIP_LINE_HEIGHT_RATIO));
5356
+ const lineWidths = result.lines.map((line) => estimateTextWidth(line, valueFontSize) + DEFAULT_STRIP_VALUE_PADDING);
5357
+ const measuredValueWidth = Math.min(
5358
+ valueWidth,
5359
+ Math.max(56, ...lineWidths)
5360
+ );
5361
+ const rowHeight = Math.round(Math.max(labelFontSize, valueFontSize) * 1.42);
5362
+ return {
5363
+ mode: result.lines.length === 1 ? "single" : "wrapped",
5364
+ label,
5365
+ rawValue,
5366
+ labelFontSize,
5140
5367
  labelWidth,
5141
- renderValue: fitTextToWidth(promptSegment.rawValue || promptSegment.value || "-", promptValueWidth, promptFontSize),
5142
- segmentWidth: labelWidth + promptValueWidth
5143
- } : null;
5144
- return [
5145
- ...promptLayout ? [promptLayout] : [],
5146
- ...fixedLayouts
5147
- ];
5368
+ valueFontSize,
5369
+ valueWidth,
5370
+ lines: result.lines,
5371
+ truncated: result.truncated,
5372
+ lineHeight,
5373
+ blockHeight: result.lines.length === 1 ? rowHeight : Math.max(lineHeight, result.lines.length * lineHeight),
5374
+ totalWidth: labelWidth + measuredValueWidth
5375
+ };
5376
+ };
5377
+ var buildStripLayout = (segments, contentWidth) => {
5378
+ const safeSegments = Array.isArray(segments) ? segments.filter(Boolean) : [];
5379
+ const promptSegment = safeSegments.find((segment) => segment.kind === "prompt") || safeSegments[0] || null;
5380
+ const metadataSegments = safeSegments.filter((segment) => segment !== promptSegment);
5381
+ const zoneGap = DEFAULT_STRIP_ZONE_GAP;
5382
+ let metadataRow = buildMetadataRowLayout(
5383
+ metadataSegments,
5384
+ Math.max(220, contentWidth)
5385
+ );
5386
+ let metadataStartX = Math.max(0, contentWidth - (metadataRow?.totalWidth || 0));
5387
+ let promptAvailableWidth = Math.max(
5388
+ 220,
5389
+ metadataStartX - zoneGap - DEFAULT_STRIP_PROMPT_TRAILING_PADDING
5390
+ );
5391
+ if (promptAvailableWidth < DEFAULT_STRIP_PROMPT_MIN_TOTAL_WIDTH && metadataSegments.length > 0) {
5392
+ const tighterMetadataWidth = Math.max(180, contentWidth - DEFAULT_STRIP_PROMPT_MIN_TOTAL_WIDTH - zoneGap);
5393
+ metadataRow = buildMetadataRowLayout(metadataSegments, tighterMetadataWidth);
5394
+ metadataStartX = Math.max(0, contentWidth - (metadataRow?.totalWidth || 0));
5395
+ promptAvailableWidth = Math.max(
5396
+ 220,
5397
+ metadataStartX - zoneGap - DEFAULT_STRIP_PROMPT_TRAILING_PADDING
5398
+ );
5399
+ }
5400
+ const preferredPromptWidth = Math.min(
5401
+ promptAvailableWidth,
5402
+ Math.max(280, Math.round(contentWidth * DEFAULT_STRIP_PROMPT_PREFERRED_WIDTH_RATIO))
5403
+ );
5404
+ let promptRow = buildPromptWrapLayout(promptSegment, preferredPromptWidth);
5405
+ if (promptRow?.truncated && promptAvailableWidth > preferredPromptWidth + 24) {
5406
+ promptRow = buildPromptWrapLayout(promptSegment, promptAvailableWidth);
5407
+ }
5408
+ const contentHeight = Math.max(
5409
+ promptRow?.blockHeight || 0,
5410
+ metadataRow?.rowHeight || 0,
5411
+ 24
5412
+ );
5413
+ const verticalPadding = contentHeight > (metadataRow?.rowHeight || 0) ? 18 : 16;
5414
+ const stripHeight = Math.max(
5415
+ contentHeight > (metadataRow?.rowHeight || 0) ? DEFAULT_STRIP_WRAPPED_MIN_HEIGHT : DEFAULT_STRIP_ONE_LINE_HEIGHT,
5416
+ Math.ceil(contentHeight + verticalPadding * 2)
5417
+ );
5418
+ return {
5419
+ mode: "zoned",
5420
+ height: stripHeight,
5421
+ contentHeight,
5422
+ promptRow,
5423
+ promptStartX: 0,
5424
+ metadataRow,
5425
+ metadataStartX
5426
+ };
5427
+ };
5428
+ var renderStripRow = (row, options = {}) => {
5429
+ const startX = Number(options.startX) || 0;
5430
+ const centerY = Number(options.centerY) || 0;
5431
+ const fontFamily = String(options.fontFamily || "");
5432
+ const showDividers = options.showDividers !== false;
5433
+ const layouts = Array.isArray(row?.layouts) ? row.layouts : [];
5434
+ if (!layouts.length) {
5435
+ return "";
5436
+ }
5437
+ const rowHeight = Math.max(18, Number(row?.rowHeight) || 18);
5438
+ const dividerHalfHeight = Math.max(12, Math.round(rowHeight * 0.56));
5439
+ const sectionGap = Math.max(0, Number(row?.sectionGap) || 0);
5440
+ const svgSegments = [];
5441
+ let currentX = startX;
5442
+ layouts.forEach((segment, index) => {
5443
+ const label = escapeXml(segment.label || "");
5444
+ const value = escapeXml(segment.renderValue || "-");
5445
+ const labelX = currentX;
5446
+ const valueX = currentX + segment.labelWidth;
5447
+ svgSegments.push(`
5448
+ <text
5449
+ x="${labelX}"
5450
+ y="${centerY}"
5451
+ fill="#111111"
5452
+ fill-opacity="0.54"
5453
+ font-family="${fontFamily}"
5454
+ font-size="${segment.labelFontSize}"
5455
+ font-weight="650"
5456
+ dominant-baseline="middle"
5457
+ >${label}</text>
5458
+ <text
5459
+ x="${valueX}"
5460
+ y="${centerY}"
5461
+ fill="#111111"
5462
+ font-family="${fontFamily}"
5463
+ font-size="${segment.valueFontSize}"
5464
+ font-weight="${segment.kind === "prompt" ? 760 : 710}"
5465
+ dominant-baseline="middle"
5466
+ >${value}</text>
5467
+ `);
5468
+ currentX += segment.segmentWidth;
5469
+ if (showDividers && index < layouts.length - 1) {
5470
+ const lineX = currentX + Math.round(sectionGap / 2);
5471
+ svgSegments.push(`
5472
+ <line
5473
+ x1="${lineX}"
5474
+ y1="${centerY - dividerHalfHeight}"
5475
+ x2="${lineX}"
5476
+ y2="${centerY + dividerHalfHeight}"
5477
+ stroke="#94a3b8"
5478
+ stroke-opacity="0.34"
5479
+ stroke-width="1"
5480
+ />
5481
+ `);
5482
+ currentX += sectionGap;
5483
+ }
5484
+ });
5485
+ return svgSegments.join("");
5486
+ };
5487
+ var renderStripDivider = (x, centerY, height) => {
5488
+ const safeX = Number(x) || 0;
5489
+ const safeCenterY = Number(centerY) || 0;
5490
+ const safeHeight = Math.max(18, Number(height) || 18);
5491
+ const halfHeight = Math.max(12, Math.round(safeHeight * 0.56));
5492
+ return `
5493
+ <line
5494
+ x1="${safeX}"
5495
+ y1="${safeCenterY - halfHeight}"
5496
+ x2="${safeX}"
5497
+ y2="${safeCenterY + halfHeight}"
5498
+ stroke="#94a3b8"
5499
+ stroke-opacity="0.34"
5500
+ stroke-width="1"
5501
+ />
5502
+ `;
5148
5503
  };
5149
5504
  var buildWatermarkifySvg = (meta, imageWidth, imageHeight) => {
5150
5505
  const hasWatermark = meta?.watermark?.enabled !== false && normalizeText(meta?.watermarkText);
@@ -5194,67 +5549,97 @@ var buildWatermarkifySvg = (meta, imageWidth, imageHeight) => {
5194
5549
  parts.push(`<g id="watermarks">${stamps.join("")}</g>`);
5195
5550
  }
5196
5551
  if (hasStrip) {
5197
- const stripHeight = DEFAULT_STRIP_HEIGHT;
5198
- const stripY = height - stripHeight;
5199
5552
  const logoSize = 28;
5200
5553
  const logoX = DEFAULT_STRIP_PADDING_LEFT;
5201
- const logoY = stripY + Math.round((stripHeight - logoSize) / 2);
5202
5554
  const contentStartX = logoX + logoSize + DEFAULT_STRIP_GAP;
5203
5555
  const contentWidth = width - contentStartX - DEFAULT_STRIP_PADDING_RIGHT;
5556
+ const stripLayout = buildStripLayout(meta.stripSegments, contentWidth);
5557
+ const stripHeight = stripLayout.height;
5558
+ const stripY = height - stripHeight;
5559
+ const logoY = Number((stripY + (stripHeight - logoSize) / 2).toFixed(2));
5560
+ let stripContentSvg = "";
5204
5561
  const centerY = stripY + stripHeight / 2 + 1;
5205
- const layouts = buildStripSegmentLayouts(meta.stripSegments, contentWidth);
5206
- const svgSegments = [];
5207
- let currentX = contentStartX;
5208
- layouts.forEach((segment, index) => {
5209
- const label = escapeXml(segment.label || "");
5210
- const value = escapeXml(segment.renderValue || "-");
5211
- const labelX = currentX;
5212
- const valueX = currentX + segment.labelWidth;
5213
- svgSegments.push(`
5562
+ const promptRow = stripLayout.promptRow;
5563
+ const metadataRow = stripLayout.metadataRow;
5564
+ const promptParts = [];
5565
+ const hasPrompt = Boolean(promptRow?.label);
5566
+ const hasMetadata = Array.isArray(metadataRow?.layouts) && metadataRow.layouts.length > 0;
5567
+ const zoneDividerX = hasPrompt && hasMetadata ? contentStartX + stripLayout.metadataStartX - DEFAULT_STRIP_ZONE_GAP / 2 : null;
5568
+ if (promptRow?.mode === "single") {
5569
+ promptParts.push(renderStripRow({
5570
+ layouts: [
5571
+ {
5572
+ kind: "prompt",
5573
+ label: promptRow.label,
5574
+ renderValue: promptRow.lines[0] || "-",
5575
+ labelFontSize: promptRow.labelFontSize,
5576
+ valueFontSize: promptRow.valueFontSize,
5577
+ labelWidth: promptRow.labelWidth,
5578
+ segmentWidth: promptRow.labelWidth + promptRow.valueWidth
5579
+ }
5580
+ ],
5581
+ sectionGap: 0,
5582
+ rowHeight: promptRow.blockHeight
5583
+ }, {
5584
+ startX: contentStartX + stripLayout.promptStartX,
5585
+ centerY,
5586
+ fontFamily,
5587
+ showDividers: false
5588
+ }));
5589
+ } else if (promptRow) {
5590
+ const promptStartX = contentStartX + stripLayout.promptStartX;
5591
+ const labelX = promptStartX;
5592
+ const valueX = promptStartX + promptRow.labelWidth;
5593
+ const blockTop = centerY - promptRow.blockHeight / 2;
5594
+ const labelY = centerY;
5595
+ promptParts.push(`
5214
5596
  <text
5215
5597
  x="${labelX}"
5216
- y="${centerY}"
5598
+ y="${labelY}"
5217
5599
  fill="#111111"
5218
- fill-opacity="0.42"
5600
+ fill-opacity="0.54"
5219
5601
  font-family="${fontFamily}"
5220
- font-size="12"
5221
- font-weight="600"
5602
+ font-size="${promptRow.labelFontSize}"
5603
+ font-weight="650"
5222
5604
  dominant-baseline="middle"
5223
- >${label}</text>
5224
- <text
5225
- x="${valueX}"
5226
- y="${centerY}"
5227
- fill="#111111"
5228
- font-family="${fontFamily}"
5229
- font-size="${segment.fontSize}"
5230
- font-weight="${segment.kind === "prompt" ? 760 : 710}"
5231
- dominant-baseline="middle"
5232
- >${value}</text>
5605
+ >${escapeXml(promptRow.label)}</text>
5233
5606
  `);
5234
- currentX += segment.segmentWidth;
5235
- if (index < layouts.length - 1) {
5236
- const lineX = currentX + 8;
5237
- svgSegments.push(`
5238
- <line
5239
- x1="${lineX}"
5240
- y1="${stripY + 26}"
5241
- x2="${lineX}"
5242
- y2="${stripY + stripHeight - 26}"
5243
- stroke="#94a3b8"
5244
- stroke-opacity="0.34"
5245
- stroke-width="1"
5246
- />
5607
+ promptRow.lines.forEach((line, index) => {
5608
+ const lineY = blockTop + index * promptRow.lineHeight + promptRow.lineHeight / 2;
5609
+ promptParts.push(`
5610
+ <text
5611
+ x="${valueX}"
5612
+ y="${lineY}"
5613
+ fill="#111111"
5614
+ font-family="${fontFamily}"
5615
+ font-size="${promptRow.valueFontSize}"
5616
+ font-weight="760"
5617
+ dominant-baseline="middle"
5618
+ >${escapeXml(line)}</text>
5247
5619
  `);
5248
- currentX += DEFAULT_STRIP_GAP + 16;
5249
- }
5250
- });
5620
+ });
5621
+ }
5622
+ stripContentSvg = [
5623
+ promptParts.join(""),
5624
+ zoneDividerX == null ? "" : renderStripDivider(
5625
+ zoneDividerX,
5626
+ centerY,
5627
+ Math.max(promptRow?.blockHeight || 0, metadataRow?.rowHeight || 0)
5628
+ ),
5629
+ renderStripRow(metadataRow, {
5630
+ startX: contentStartX + stripLayout.metadataStartX,
5631
+ centerY,
5632
+ fontFamily,
5633
+ showDividers: true
5634
+ })
5635
+ ].join("");
5251
5636
  parts.push(`
5252
5637
  <g id="strip">
5253
5638
  <rect x="0" y="${stripY}" width="${width}" height="${stripHeight}" fill="#ffffff" fill-opacity="0.985" />
5254
5639
  <rect x="0" y="${stripY}" width="${width}" height="1" fill="#111111" fill-opacity="0.10" />
5255
5640
  <rect x="${DEFAULT_STRIP_PADDING_LEFT}" y="${stripY}" width="${Math.max(120, Math.round(width * 0.42))}" height="2" fill="url(#stripAccent)" />
5256
5641
  ${meta.stripLogoSrc ? `<image href="${escapeXml(meta.stripLogoSrc)}" x="${logoX}" y="${logoY}" width="${logoSize}" height="${logoSize}" />` : ""}
5257
- ${svgSegments.join("")}
5642
+ ${stripContentSvg}
5258
5643
  </g>
5259
5644
  `);
5260
5645
  }
@@ -5370,6 +5755,20 @@ var normalizeShare = (share) => {
5370
5755
  xurl: normalizeXurl(source.xurl)
5371
5756
  };
5372
5757
  };
5758
+ var resolveCaptureScreenWatermarkify = (page, optionValue) => {
5759
+ if (optionValue === false) {
5760
+ return normalizeScreenshotWatermarkify(false);
5761
+ }
5762
+ const runtimeQuery = String(page?.[PageRuntimeStateKey]?.query || "").trim();
5763
+ if (optionValue == null || optionValue === true) {
5764
+ return normalizeScreenshotWatermarkify(runtimeQuery ? { query: runtimeQuery } : true);
5765
+ }
5766
+ const optionSource = optionValue && typeof optionValue === "object" ? optionValue : {};
5767
+ return normalizeScreenshotWatermarkify({
5768
+ ...runtimeQuery ? { query: runtimeQuery } : {},
5769
+ ...optionSource
5770
+ });
5771
+ };
5373
5772
  var getByPathSegments = (source, pathSegments) => {
5374
5773
  if (!Array.isArray(pathSegments) || pathSegments.length === 0) return void 0;
5375
5774
  let current = source;
@@ -5693,12 +6092,12 @@ var Share = {
5693
6092
  * @returns {Promise<string>} base64 png
5694
6093
  */
5695
6094
  async captureScreen(page, options = {}) {
5696
- const originalViewport = page.viewportSize();
5697
- const defaultBuffer = Math.round((originalViewport?.height || 1080) / 2);
6095
+ const originalViewport = await resolveCurrentViewportSize(page);
6096
+ const defaultBuffer = Math.round((originalViewport.height || 1080) / 2);
5698
6097
  const buffer = options.buffer ?? defaultBuffer;
5699
6098
  const restore = options.restore ?? false;
5700
6099
  const maxHeight = options.maxHeight ?? 8e3;
5701
- const screenshotWatermarkify = normalizeScreenshotWatermarkify(options.watermarkify ?? true);
6100
+ const screenshotWatermarkify = resolveCaptureScreenWatermarkify(page, options.watermarkify);
5702
6101
  try {
5703
6102
  const maxScrollHeight = await page.evaluate(() => {
5704
6103
  let maxHeight2 = document.body.scrollHeight;
@@ -5722,7 +6121,7 @@ var Share = {
5722
6121
  });
5723
6122
  const targetHeight = Math.min(maxScrollHeight + buffer, maxHeight);
5724
6123
  await page.setViewportSize({
5725
- width: originalViewport?.width || 1280,
6124
+ width: originalViewport.width,
5726
6125
  height: targetHeight
5727
6126
  });
5728
6127
  await (0, import_delay2.default)(1e3);
@@ -5754,9 +6153,7 @@ var Share = {
5754
6153
  el.classList.remove("__pk_expanded__");
5755
6154
  });
5756
6155
  });
5757
- if (originalViewport) {
5758
- await page.setViewportSize(originalViewport);
5759
- }
6156
+ await page.setViewportSize(originalViewport);
5760
6157
  }
5761
6158
  }
5762
6159
  }