@skrillex1224/playwright-toolkit 2.1.208 → 2.1.210

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
@@ -4549,7 +4549,7 @@ var Logger = {
4549
4549
  var import_delay2 = __toESM(require("delay"), 1);
4550
4550
 
4551
4551
  // src/internals/watermarkify.js
4552
- var SCREENSHOT_WATERMARKIFY_HOST_ID = "__pk_screenshot_watermarkify__";
4552
+ var import_sharp = __toESM(require("sharp"), 1);
4553
4553
  var DEFAULT_TIMEZONE_OFFSET = 8;
4554
4554
  var DEFAULT_RESOLVER_TIMEOUT_MS = 180;
4555
4555
  var DEFAULT_IP_LOOKUP_TIMEOUT_MS = 1e4;
@@ -4560,6 +4560,11 @@ var DEFAULT_WATERMARK_CELL_HEIGHT = 330;
4560
4560
  var DEFAULT_STRIP_LOGO_URL = "https://static.heartbitai.com/geo/icon/favicon.png";
4561
4561
  var DEFAULT_IP_LOOKUP_URL = "http://myip.ipip.net";
4562
4562
  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;
4567
+ var DEFAULT_STRIP_LABEL_WIDTH = 56;
4563
4568
  var WEAK_LOCATION_VALUES = /* @__PURE__ */ new Set(["cn", "\u4E2D\u56FD"]);
4564
4569
  var LOCATION_NETWORK_SUFFIX_PATTERNS = [
4565
4570
  /(?:中国)?移动$/i,
@@ -4595,13 +4600,6 @@ var shortenTail = (value, maxLen = 80) => {
4595
4600
  if (!text || text.length <= maxLen) return text;
4596
4601
  return `${text.slice(0, Math.max(0, maxLen - 1)).trimEnd()}\u2026`;
4597
4602
  };
4598
- var shortenMiddle = (value, maxLen = 56, headLen = 32, tailLen = 14) => {
4599
- const text = toInline(value, Math.max(maxLen * 2, maxLen + headLen + tailLen));
4600
- if (!text || text.length <= maxLen) return text;
4601
- const safeHeadLen = Math.max(4, Math.min(headLen, maxLen - 5));
4602
- const safeTailLen = Math.max(4, Math.min(tailLen, maxLen - safeHeadLen - 1));
4603
- return `${text.slice(0, safeHeadLen).trimEnd()}\u2026${text.slice(-safeTailLen).trimStart()}`;
4604
- };
4605
4603
  var padDatePart = (value) => String(value).padStart(2, "0");
4606
4604
  var formatUtcOffsetLabel = (offsetHours = DEFAULT_TIMEZONE_OFFSET) => {
4607
4605
  const sign = offsetHours >= 0 ? "+" : "-";
@@ -4777,8 +4775,8 @@ var resolveWithCustomResolver = async (page, baseMeta, options = {}) => {
4777
4775
  url: baseMeta.url,
4778
4776
  hostname: baseMeta.hostname,
4779
4777
  title: baseMeta.title,
4778
+ prompt: baseMeta.prompt,
4780
4779
  query: baseMeta.query,
4781
- taskId: baseMeta.taskId,
4782
4780
  serverAddr,
4783
4781
  signal: controller?.signal
4784
4782
  })).catch(() => null),
@@ -4914,22 +4912,23 @@ var resolveEnrichment = async (page, baseMeta, options) => {
4914
4912
  }
4915
4913
  return merged;
4916
4914
  };
4917
- var buildWatermarkStamp = ({ taskId, captureTime, ip, location }) => {
4915
+ var buildWatermarkStamp = ({ prompt, captureTime, ip, location }) => {
4918
4916
  const parts = [
4919
- `TaskID ${shortenMiddle(taskId, 56, 24, 16) || "-"}`,
4917
+ `Prompt ${shortenTail(prompt, 40) || "-"}`,
4920
4918
  `Time ${captureTime}`,
4921
4919
  `Loc ${shortenTail(location, 20) || "-"}`,
4922
4920
  `IP ${toInline(ip, 24) || "-"}`
4923
4921
  ];
4924
4922
  return parts.join(" | ");
4925
4923
  };
4926
- var buildStripSegments = ({ taskId, captureTime, ip, location }) => {
4924
+ var buildStripSegments = ({ prompt, captureTime, ip, location }) => {
4925
+ const promptValue = toInline(prompt, 240) || "-";
4927
4926
  return [
4928
4927
  {
4929
- kind: "taskId",
4930
- label: "TaskID",
4931
- value: toInline(taskId, 240) || "-",
4932
- rawValue: toInline(taskId, 240) || "-"
4928
+ kind: "prompt",
4929
+ label: "Prompt",
4930
+ value: promptValue,
4931
+ rawValue: promptValue
4933
4932
  },
4934
4933
  {
4935
4934
  kind: "time",
@@ -4951,14 +4950,58 @@ var buildStripSegments = ({ taskId, captureTime, ip, location }) => {
4951
4950
  }
4952
4951
  ];
4953
4952
  };
4953
+ var escapeXml = (value) => String(value || "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
4954
+ var estimateTextWidth = (value, fontSize = 16) => {
4955
+ const text = String(value || "");
4956
+ let width = 0;
4957
+ for (const char of text) {
4958
+ if (/\s/.test(char)) {
4959
+ width += fontSize * 0.34;
4960
+ } else if (/[\u4e00-\u9fff\u3400-\u4dbf\u3040-\u30ff\uac00-\ud7af]/.test(char)) {
4961
+ width += fontSize * 0.92;
4962
+ } else if (/[A-Z0-9]/.test(char)) {
4963
+ width += fontSize * 0.62;
4964
+ } else {
4965
+ width += fontSize * 0.56;
4966
+ }
4967
+ }
4968
+ return Math.ceil(width);
4969
+ };
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
+ var resolvePromptFields = (options = {}, fallbackTitle = "") => {
4990
+ const query = normalizeText(options.query);
4991
+ const prompt = normalizeText(options.prompt) || query || normalizeText(fallbackTitle) || "\u672A\u63D0\u4F9B Prompt";
4992
+ return {
4993
+ prompt,
4994
+ query: query || prompt
4995
+ };
4996
+ };
4954
4997
  var createBaseWatermarkifyOptions = () => ({
4955
4998
  enabled: true,
4956
4999
  timezoneOffsetHours: DEFAULT_TIMEZONE_OFFSET,
4957
5000
  response: null,
4958
5001
  ip: "",
4959
5002
  location: "",
5003
+ prompt: "",
4960
5004
  query: "",
4961
- taskId: "",
4962
5005
  ipLookup: true,
4963
5006
  ipLookupTimeoutMs: DEFAULT_IP_LOOKUP_TIMEOUT_MS,
4964
5007
  resolver: null,
@@ -5001,8 +5044,8 @@ var normalizeScreenshotWatermarkify = (value) => {
5001
5044
  response: source.response ?? null,
5002
5045
  ip: normalizeText(source.ip),
5003
5046
  location: normalizeText(source.location),
5004
- query: toInline(source.query, 140),
5005
- taskId: toInline(source.taskId, 120),
5047
+ prompt: toInline(source.prompt, 220),
5048
+ query: toInline(source.query, 220),
5006
5049
  ipLookup: source.ipLookup !== false,
5007
5050
  ipLookupTimeoutMs: Number.isFinite(ipLookupTimeoutMsRaw) ? ipLookupTimeoutMsRaw : DEFAULT_IP_LOOKUP_TIMEOUT_MS,
5008
5051
  resolver: typeof source.resolver === "function" ? source.resolver : null,
@@ -5021,16 +5064,16 @@ var resolveScreenshotWatermarkifyMeta = async (page, options = {}) => {
5021
5064
  const url = normalizeText(page?.url?.()) || "about:blank";
5022
5065
  const title = normalizeText(await page.title().catch(() => "")) || "\u672A\u547D\u540D\u9875\u9762";
5023
5066
  const hostname = getHostname(url);
5024
- const query = normalizeText(options.query) || title || "\u672A\u63D0\u4F9B Query";
5025
- const taskId = normalizeText(options.taskId) || "-";
5026
- const captureTime = formatTimestampForUtcOffset(/* @__PURE__ */ new Date(), timezoneOffsetHours);
5067
+ const { prompt, query } = resolvePromptFields(options, title);
5068
+ const capturedAt = options.capturedAt instanceof Date ? options.capturedAt : new Date(options.capturedAt || Date.now());
5069
+ const captureTime = formatTimestampForUtcOffset(capturedAt, timezoneOffsetHours);
5027
5070
  const [enrichment, stripLogoSrc] = await Promise.all([
5028
5071
  resolveEnrichment(page, {
5029
5072
  url,
5030
5073
  hostname,
5031
5074
  title,
5032
- query,
5033
- taskId
5075
+ prompt,
5076
+ query
5034
5077
  }, options),
5035
5078
  resolveStripLogoSrc()
5036
5079
  ]);
@@ -5038,13 +5081,13 @@ var resolveScreenshotWatermarkifyMeta = async (page, options = {}) => {
5038
5081
  const location = enrichment.location || "-";
5039
5082
  return {
5040
5083
  watermarkText: buildWatermarkStamp({
5041
- taskId,
5084
+ prompt,
5042
5085
  captureTime,
5043
5086
  ip,
5044
5087
  location
5045
5088
  }),
5046
5089
  stripSegments: buildStripSegments({
5047
- taskId,
5090
+ prompt,
5048
5091
  captureTime,
5049
5092
  ip,
5050
5093
  location
@@ -5054,395 +5097,199 @@ var resolveScreenshotWatermarkifyMeta = async (page, options = {}) => {
5054
5097
  stripLogoSrc
5055
5098
  };
5056
5099
  };
5057
- var installScreenshotWatermarkify = async (page, meta) => {
5100
+ var buildFontFamily = () => 'MiSans, "SF Pro Display", "PingFang SC", "Helvetica Neue", Arial, sans-serif';
5101
+ var buildStripSegmentLayouts = (segments, contentWidth) => {
5102
+ 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
5110
+ };
5111
+ const maxValueWidths = {
5112
+ time: 290,
5113
+ location: 220,
5114
+ ip: 160
5115
+ };
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
+ };
5130
+ });
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)
5136
+ );
5137
+ const promptLayout = promptSegment ? {
5138
+ ...promptSegment,
5139
+ fontSize: promptFontSize,
5140
+ labelWidth,
5141
+ renderValue: fitTextToWidth(promptSegment.rawValue || promptSegment.value || "-", promptValueWidth, promptFontSize),
5142
+ segmentWidth: labelWidth + promptValueWidth
5143
+ } : null;
5144
+ return [
5145
+ ...promptLayout ? [promptLayout] : [],
5146
+ ...fixedLayouts
5147
+ ];
5148
+ };
5149
+ var buildWatermarkifySvg = (meta, imageWidth, imageHeight) => {
5058
5150
  const hasWatermark = meta?.watermark?.enabled !== false && normalizeText(meta?.watermarkText);
5059
5151
  const hasStrip = meta?.strip?.enabled !== false && Array.isArray(meta?.stripSegments) && meta.stripSegments.length > 0;
5060
- if (!page || !meta || !hasWatermark && !hasStrip) {
5061
- return async () => {
5062
- };
5152
+ if (!hasWatermark && !hasStrip) {
5153
+ return "";
5063
5154
  }
5064
- await page.evaluate(({ hostId, watermarkifyMeta, defaults }) => {
5065
- const previousHost = document.getElementById(hostId);
5066
- if (previousHost && typeof previousHost.__pkCleanup === "function") {
5067
- previousHost.__pkCleanup();
5068
- }
5069
- previousHost?.remove();
5070
- const mountPoint = document.body || document.documentElement;
5071
- if (!mountPoint) return;
5072
- const createNode = (tagName, className) => {
5073
- const node = document.createElement(tagName);
5074
- if (className) node.className = className;
5075
- return node;
5076
- };
5077
- const safeText = (value) => String(value || "").trim();
5078
- const host = document.createElement("div");
5079
- host.id = hostId;
5080
- host.style.position = "fixed";
5081
- host.style.inset = "0";
5082
- host.style.width = "100vw";
5083
- host.style.height = "100vh";
5084
- host.style.pointerEvents = "none";
5085
- host.style.zIndex = "2147483646";
5086
- const shadow = host.attachShadow({ mode: "open" });
5087
- const style = document.createElement("style");
5088
- style.textContent = `
5089
- :host {
5090
- all: initial;
5091
- position: fixed;
5092
- inset: 0;
5093
- width: 100vw;
5094
- height: 100vh;
5095
- pointer-events: none;
5096
- z-index: 2147483646;
5097
- }
5098
-
5099
- .root {
5100
- position: absolute;
5101
- inset: 0;
5102
- width: 100%;
5103
- height: 100%;
5104
- overflow: hidden;
5105
- pointer-events: none;
5106
- font-family: MiSans, "SF Pro Display", "PingFang SC", "Helvetica Neue", Arial, sans-serif;
5107
- }
5108
-
5109
- .watermark {
5110
- position: absolute;
5111
- inset: 0;
5112
- overflow: hidden;
5113
- pointer-events: none;
5114
- }
5115
-
5116
- .wmStamp {
5117
- position: absolute;
5118
- left: 0;
5119
- top: 0;
5120
- transform-origin: left center;
5121
- pointer-events: none;
5122
- white-space: nowrap;
5123
- }
5124
-
5125
- .wmStampText {
5126
- color: rgba(17, 17, 17, var(--wm-opacity, 0.15));
5127
- font-size: 19px;
5128
- font-weight: 720;
5129
- line-height: 1;
5130
- letter-spacing: 0.032em;
5131
- text-shadow: 0 1px 0 rgba(255, 255, 255, 0.44);
5132
- -webkit-text-stroke: 0.22px rgba(255, 255, 255, 0.12);
5133
- font-variant-numeric: tabular-nums;
5134
- filter: saturate(0.92);
5135
- }
5136
-
5137
- .strip {
5138
- position: absolute;
5139
- left: 0;
5140
- right: 0;
5141
- bottom: 0;
5142
- min-height: 78px;
5143
- padding: 0 22px 0 20px;
5144
- display: flex;
5145
- align-items: center;
5146
- overflow: hidden;
5147
- border-top: 1px solid rgba(17, 17, 17, 0.1);
5148
- background:
5149
- linear-gradient(180deg, rgba(255, 255, 255, 0.995) 0%, rgba(248, 248, 247, 0.985) 100%);
5150
- box-shadow:
5151
- 0 -22px 48px rgba(15, 23, 42, 0.1),
5152
- inset 0 1px 0 rgba(255, 255, 255, 0.92);
5153
- backdrop-filter: blur(16px);
5154
- -webkit-backdrop-filter: blur(16px);
5155
- }
5156
-
5157
- .strip::before {
5158
- content: "";
5159
- position: absolute;
5160
- left: 20px;
5161
- right: 52%;
5162
- top: 0;
5163
- height: 2px;
5164
- border-radius: 999px;
5165
- background: linear-gradient(
5166
- 90deg,
5167
- rgba(17, 17, 17, 0.98) 0%,
5168
- rgba(17, 17, 17, 0.68) 24%,
5169
- rgba(17, 17, 17, 0.08) 58%,
5170
- rgba(17, 17, 17, 0) 100%
5171
- );
5172
- }
5173
-
5174
- .stripInner {
5175
- position: relative;
5176
- z-index: 1;
5177
- display: flex;
5178
- align-items: center;
5179
- gap: 16px;
5180
- width: 100%;
5181
- min-width: 0;
5182
- }
5183
-
5184
- .stripLogo {
5185
- position: relative;
5186
- width: 28px;
5187
- height: 28px;
5188
- flex: none;
5189
- display: flex;
5190
- align-items: center;
5191
- justify-content: center;
5192
- }
5193
-
5194
- .stripLogoImg {
5195
- width: 28px;
5196
- height: 28px;
5197
- object-fit: contain;
5198
- display: block;
5199
- }
5200
-
5201
- .segments {
5202
- display: flex;
5203
- align-items: center;
5204
- gap: 0;
5205
- flex: 1 1 auto;
5206
- min-width: 0;
5207
- white-space: nowrap;
5208
- }
5209
-
5210
- .segment {
5211
- position: relative;
5212
- display: inline-flex;
5213
- align-items: center;
5214
- gap: 10px;
5215
- min-width: 0;
5216
- flex: none;
5217
- padding-right: 16px;
5218
- margin-right: 16px;
5219
- }
5220
-
5221
- .segment:not(:last-child)::after {
5222
- content: "";
5223
- position: absolute;
5224
- top: 50%;
5225
- right: 0;
5226
- width: 1px;
5227
- height: 25px;
5228
- transform: translateY(-50%);
5229
- background: linear-gradient(
5230
- 180deg,
5231
- rgba(148, 163, 184, 0) 0%,
5232
- rgba(148, 163, 184, 0.34) 24%,
5233
- rgba(148, 163, 184, 0.34) 76%,
5234
- rgba(148, 163, 184, 0) 100%
5235
- );
5236
- }
5237
-
5238
- .segmentTaskId {
5239
- flex: 1 1 auto;
5240
- max-width: none;
5241
- min-width: 0;
5242
- }
5243
-
5244
- .label {
5245
- width: 52px;
5246
- color: rgba(17, 17, 17, 0.42);
5247
- font-size: 12px;
5248
- font-weight: 600;
5249
- line-height: 1;
5250
- letter-spacing: 0.01em;
5251
- flex: none;
5252
- display: inline-flex;
5253
- align-items: center;
5254
- justify-content: flex-start;
5255
- }
5256
-
5257
- .value {
5258
- color: #111111;
5259
- font-size: 18px;
5260
- font-weight: 710;
5261
- line-height: 1;
5262
- min-width: 0;
5263
- overflow: hidden;
5264
- text-overflow: ellipsis;
5265
- font-variant-numeric: tabular-nums;
5266
- display: inline-flex;
5267
- align-items: center;
5268
- }
5269
-
5270
- .segmentTaskId .value {
5271
- color: #101010;
5272
- font-size: 15px;
5273
- font-weight: 760;
5274
- letter-spacing: -0.01em;
5275
- }
5276
-
5277
- .segmentTime .value {
5278
- color: #202020;
5279
- font-size: 17px;
5280
- font-weight: 700;
5281
- }
5282
-
5283
- .segmentLocation .value,
5284
- .segmentIp .value {
5285
- color: #2a2a2a;
5286
- font-size: 17px;
5287
- font-weight: 700;
5288
- }
5289
-
5290
- `;
5291
- const root = createNode("div", "root");
5292
- const watermarkNode = createNode("div", "watermark");
5293
- const stripNode = createNode("div", "strip");
5294
- const stripInner = createNode("div", "stripInner");
5295
- const stripLogo = createNode("div", "stripLogo");
5296
- const stripLogoImg = createNode("img", "stripLogoImg");
5297
- const stripSegmentsNode = createNode("div", "segments");
5298
- stripLogoImg.alt = "";
5299
- stripLogoImg.decoding = "async";
5300
- stripLogoImg.referrerPolicy = "no-referrer";
5301
- stripLogoImg.src = safeText(watermarkifyMeta?.stripLogoSrc) || defaults.logoUrl;
5302
- stripLogo.appendChild(stripLogoImg);
5303
- stripInner.appendChild(stripLogo);
5304
- stripInner.appendChild(stripSegmentsNode);
5305
- stripNode.appendChild(stripInner);
5306
- root.appendChild(watermarkNode);
5307
- root.appendChild(stripNode);
5308
- shadow.appendChild(style);
5309
- shadow.appendChild(root);
5310
- mountPoint.appendChild(host);
5311
- const buildSegmentNode = (segment) => {
5312
- const kind = String(segment?.kind || "").trim();
5313
- const label = String(segment?.label || "").trim();
5314
- const value = String(segment?.value || "").trim();
5315
- const rawValue = String(segment?.rawValue || value).trim();
5316
- const classSuffix = kind ? ` segment${kind.slice(0, 1).toUpperCase()}${kind.slice(1)}` : "";
5317
- const segmentNode = createNode("span", `segment${classSuffix}`);
5318
- const labelNode = createNode("span", "label");
5319
- const valueNode = createNode("span", "value");
5320
- labelNode.textContent = label;
5321
- valueNode.textContent = value;
5322
- segmentNode.title = rawValue ? `${label} ${rawValue}` : label;
5323
- segmentNode.appendChild(labelNode);
5324
- segmentNode.appendChild(valueNode);
5325
- return segmentNode;
5326
- };
5327
- const renderStrip = () => {
5328
- const stripSegments = Array.isArray(watermarkifyMeta?.stripSegments) ? watermarkifyMeta.stripSegments : [];
5329
- if (!watermarkifyMeta?.strip?.enabled || stripSegments.length === 0) {
5330
- stripNode.remove();
5331
- return;
5332
- }
5333
- const fragment = document.createDocumentFragment();
5334
- for (const segment of stripSegments) {
5335
- fragment.appendChild(buildSegmentNode(segment));
5336
- }
5337
- stripSegmentsNode.replaceChildren(fragment);
5338
- };
5339
- const waitForLogo = async () => {
5340
- if (!stripLogoImg.getAttribute("src")) {
5341
- return;
5342
- }
5343
- await new Promise((resolve) => {
5344
- let settled = false;
5345
- const done = () => {
5346
- if (settled) return;
5347
- settled = true;
5348
- resolve();
5349
- };
5350
- const fail = () => {
5351
- if (settled) return;
5352
- stripLogo.remove();
5353
- done();
5354
- };
5355
- const timeoutDone = () => {
5356
- if (settled) return;
5357
- done();
5358
- };
5359
- if (stripLogoImg.complete && stripLogoImg.naturalWidth > 0) {
5360
- done();
5361
- return;
5362
- }
5363
- if (stripLogoImg.complete && stripLogoImg.naturalWidth === 0) {
5364
- fail();
5365
- return;
5366
- }
5367
- stripLogoImg.addEventListener("load", done, { once: true });
5368
- stripLogoImg.addEventListener("error", fail, { once: true });
5369
- setTimeout(timeoutDone, 2500);
5370
- });
5371
- };
5372
- const renderWatermark = () => {
5373
- const stampText = safeText(watermarkifyMeta?.watermarkText);
5374
- if (!watermarkifyMeta?.watermark?.enabled || !stampText) {
5375
- watermarkNode.remove();
5376
- return;
5377
- }
5378
- const cellWidth = Math.max(680, Number(watermarkifyMeta.watermark.cellWidth) || defaults.cellWidth);
5379
- const cellHeight = Math.max(260, Number(watermarkifyMeta.watermark.cellHeight) || defaults.cellHeight);
5380
- const rotateDeg = Number(watermarkifyMeta.watermark.rotateDeg) || defaults.rotateDeg;
5381
- const opacity = Math.min(0.22, Math.max(0.05, Number(watermarkifyMeta.watermark.opacity) || defaults.opacity));
5382
- const width = watermarkNode.clientWidth || window.innerWidth || document.documentElement.clientWidth || cellWidth;
5383
- const height = watermarkNode.clientHeight || window.innerHeight || document.documentElement.clientHeight || cellHeight;
5384
- const rowOffset = Math.round(cellWidth * 0.24);
5385
- const startX = -Math.round(cellWidth * 0.16);
5386
- const startY = -Math.round(cellHeight * 0.12);
5387
- const cols = Math.ceil((width + cellWidth * 1.1) / cellWidth) + 1;
5388
- const rows = Math.ceil((height + cellHeight * 0.9) / cellHeight) + 1;
5389
- const fragment = document.createDocumentFragment();
5390
- for (let row = 0; row < rows; row += 1) {
5391
- for (let col = 0; col < cols; col += 1) {
5392
- const stamp = createNode("div", "wmStamp");
5393
- const stampTextNode = createNode("div", "wmStampText");
5394
- const x = startX + col * cellWidth + (row % 2 ? rowOffset : 0);
5395
- const y = startY + row * cellHeight + (row % 2 ? 14 : -8);
5396
- stamp.style.transform = `translate(${x}px, ${y}px) rotate(${rotateDeg}deg)`;
5397
- stampTextNode.style.setProperty("--wm-opacity", String(opacity));
5398
- stampTextNode.textContent = stampText;
5399
- stamp.appendChild(stampTextNode);
5400
- fragment.appendChild(stamp);
5401
- }
5155
+ const width = Math.max(1, Number(imageWidth) || 1);
5156
+ const height = Math.max(1, Number(imageHeight) || 1);
5157
+ const fontFamily = escapeXml(buildFontFamily());
5158
+ const parts = [];
5159
+ if (hasWatermark) {
5160
+ const stampText = escapeXml(normalizeText(meta.watermarkText));
5161
+ const cellWidth = Math.max(680, Number(meta.watermark?.cellWidth) || DEFAULT_WATERMARK_CELL_WIDTH);
5162
+ const cellHeight = Math.max(260, Number(meta.watermark?.cellHeight) || DEFAULT_WATERMARK_CELL_HEIGHT);
5163
+ const rotateDeg = Number(meta.watermark?.rotateDeg) || DEFAULT_WATERMARK_ROTATE_DEG;
5164
+ const opacity = Math.min(0.22, Math.max(0.05, Number(meta.watermark?.opacity) || DEFAULT_WATERMARK_OPACITY));
5165
+ const rowOffset = Math.round(cellWidth * 0.24);
5166
+ const startX = -Math.round(cellWidth * 0.16);
5167
+ const startY = -Math.round(cellHeight * 0.12);
5168
+ const cols = Math.ceil((width + cellWidth * 1.1) / cellWidth) + 1;
5169
+ const rows = Math.ceil((height + cellHeight * 0.9) / cellHeight) + 1;
5170
+ const stamps = [];
5171
+ for (let row = 0; row < rows; row += 1) {
5172
+ for (let col = 0; col < cols; col += 1) {
5173
+ const x = startX + col * cellWidth + (row % 2 ? rowOffset : 0);
5174
+ const y = startY + row * cellHeight + (row % 2 ? 14 : -8);
5175
+ stamps.push(`
5176
+ <g transform="translate(${x} ${y}) rotate(${rotateDeg})">
5177
+ <text
5178
+ x="0"
5179
+ y="0"
5180
+ fill="#111111"
5181
+ fill-opacity="${opacity}"
5182
+ stroke="#ffffff"
5183
+ stroke-opacity="0.12"
5184
+ stroke-width="0.22"
5185
+ font-family="${fontFamily}"
5186
+ font-size="19"
5187
+ font-weight="720"
5188
+ letter-spacing="0.6"
5189
+ >${stampText}</text>
5190
+ </g>
5191
+ `);
5402
5192
  }
5403
- watermarkNode.replaceChildren(fragment);
5404
- };
5405
- let resizeFrame = 0;
5406
- const queueWatermarkRender = () => {
5407
- if (!watermarkNode.isConnected) return;
5408
- cancelAnimationFrame(resizeFrame);
5409
- resizeFrame = requestAnimationFrame(() => {
5410
- renderWatermark();
5411
- });
5412
- };
5413
- const handleResize = () => {
5414
- queueWatermarkRender();
5415
- };
5416
- host.__pkCleanup = () => {
5417
- cancelAnimationFrame(resizeFrame);
5418
- window.removeEventListener("resize", handleResize);
5419
- };
5420
- renderStrip();
5421
- renderWatermark();
5422
- queueWatermarkRender();
5423
- window.addEventListener("resize", handleResize, { passive: true });
5424
- return waitForLogo();
5425
- }, {
5426
- hostId: SCREENSHOT_WATERMARKIFY_HOST_ID,
5427
- watermarkifyMeta: meta,
5428
- defaults: {
5429
- opacity: DEFAULT_WATERMARK_OPACITY,
5430
- rotateDeg: DEFAULT_WATERMARK_ROTATE_DEG,
5431
- cellWidth: DEFAULT_WATERMARK_CELL_WIDTH,
5432
- cellHeight: DEFAULT_WATERMARK_CELL_HEIGHT,
5433
- logoUrl: DEFAULT_STRIP_LOGO_URL
5434
5193
  }
5435
- });
5436
- return async () => {
5437
- await page.evaluate((hostId) => {
5438
- const host = document.getElementById(hostId);
5439
- if (host && typeof host.__pkCleanup === "function") {
5440
- host.__pkCleanup();
5194
+ parts.push(`<g id="watermarks">${stamps.join("")}</g>`);
5195
+ }
5196
+ if (hasStrip) {
5197
+ const stripHeight = DEFAULT_STRIP_HEIGHT;
5198
+ const stripY = height - stripHeight;
5199
+ const logoSize = 28;
5200
+ const logoX = DEFAULT_STRIP_PADDING_LEFT;
5201
+ const logoY = stripY + Math.round((stripHeight - logoSize) / 2);
5202
+ const contentStartX = logoX + logoSize + DEFAULT_STRIP_GAP;
5203
+ const contentWidth = width - contentStartX - DEFAULT_STRIP_PADDING_RIGHT;
5204
+ 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(`
5214
+ <text
5215
+ x="${labelX}"
5216
+ y="${centerY}"
5217
+ fill="#111111"
5218
+ fill-opacity="0.42"
5219
+ font-family="${fontFamily}"
5220
+ font-size="12"
5221
+ font-weight="600"
5222
+ 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>
5233
+ `);
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
+ />
5247
+ `);
5248
+ currentX += DEFAULT_STRIP_GAP + 16;
5441
5249
  }
5442
- host?.remove();
5443
- }, SCREENSHOT_WATERMARKIFY_HOST_ID).catch(() => {
5444
5250
  });
5445
- };
5251
+ parts.push(`
5252
+ <g id="strip">
5253
+ <rect x="0" y="${stripY}" width="${width}" height="${stripHeight}" fill="#ffffff" fill-opacity="0.985" />
5254
+ <rect x="0" y="${stripY}" width="${width}" height="1" fill="#111111" fill-opacity="0.10" />
5255
+ <rect x="${DEFAULT_STRIP_PADDING_LEFT}" y="${stripY}" width="${Math.max(120, Math.round(width * 0.42))}" height="2" fill="url(#stripAccent)" />
5256
+ ${meta.stripLogoSrc ? `<image href="${escapeXml(meta.stripLogoSrc)}" x="${logoX}" y="${logoY}" width="${logoSize}" height="${logoSize}" />` : ""}
5257
+ ${svgSegments.join("")}
5258
+ </g>
5259
+ `);
5260
+ }
5261
+ return `
5262
+ <svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
5263
+ <defs>
5264
+ <linearGradient id="stripAccent" x1="0%" y1="0%" x2="100%" y2="0%">
5265
+ <stop offset="0%" stop-color="#111111" stop-opacity="0.98" />
5266
+ <stop offset="24%" stop-color="#111111" stop-opacity="0.68" />
5267
+ <stop offset="58%" stop-color="#111111" stop-opacity="0.08" />
5268
+ <stop offset="100%" stop-color="#111111" stop-opacity="0" />
5269
+ </linearGradient>
5270
+ </defs>
5271
+ ${parts.join("")}
5272
+ </svg>
5273
+ `;
5274
+ };
5275
+ var watermarkifyScreenshotBuffer = async (buffer, meta) => {
5276
+ const hasWatermark = meta?.watermark?.enabled !== false && normalizeText(meta?.watermarkText);
5277
+ const hasStrip = meta?.strip?.enabled !== false && Array.isArray(meta?.stripSegments) && meta.stripSegments.length > 0;
5278
+ if (!Buffer.isBuffer(buffer) || !meta || !hasWatermark && !hasStrip) {
5279
+ return buffer;
5280
+ }
5281
+ const image = (0, import_sharp.default)(buffer, { failOn: "none" });
5282
+ const metadata = await image.metadata().catch(() => null);
5283
+ const width = Math.max(1, Number(metadata?.width) || 0);
5284
+ const height = Math.max(1, Number(metadata?.height) || 0);
5285
+ if (!width || !height) {
5286
+ return buffer;
5287
+ }
5288
+ const overlaySvg = buildWatermarkifySvg(meta, width, height);
5289
+ if (!overlaySvg) {
5290
+ return buffer;
5291
+ }
5292
+ return await image.composite([{ input: Buffer.from(overlaySvg), top: 0, left: 0 }]).png().toBuffer();
5446
5293
  };
5447
5294
 
5448
5295
  // src/share.js
@@ -5852,8 +5699,6 @@ var Share = {
5852
5699
  const restore = options.restore ?? false;
5853
5700
  const maxHeight = options.maxHeight ?? 8e3;
5854
5701
  const screenshotWatermarkify = normalizeScreenshotWatermarkify(options.watermarkify ?? true);
5855
- let cleanupScreenshotWatermarkify = async () => {
5856
- };
5857
5702
  try {
5858
5703
  const maxScrollHeight = await page.evaluate(() => {
5859
5704
  let maxHeight2 = document.body.scrollHeight;
@@ -5881,19 +5726,22 @@ var Share = {
5881
5726
  height: targetHeight
5882
5727
  });
5883
5728
  await (0, import_delay2.default)(1e3);
5884
- if (screenshotWatermarkify.enabled) {
5885
- const watermarkifyMeta = await resolveScreenshotWatermarkifyMeta(page, screenshotWatermarkify);
5886
- cleanupScreenshotWatermarkify = await installScreenshotWatermarkify(page, watermarkifyMeta);
5887
- await (0, import_delay2.default)(120);
5888
- }
5889
- const buffer_ = await capturePageScreenshot(page, {
5729
+ const capturedAt = /* @__PURE__ */ new Date();
5730
+ const rawBuffer = await capturePageScreenshot(page, {
5890
5731
  fullPage: true,
5891
5732
  type: "png",
5892
5733
  maxClipHeight: targetHeight
5893
5734
  });
5735
+ if (!screenshotWatermarkify.enabled) {
5736
+ return rawBuffer.toString("base64");
5737
+ }
5738
+ const watermarkifyMeta = await resolveScreenshotWatermarkifyMeta(page, {
5739
+ ...screenshotWatermarkify,
5740
+ capturedAt
5741
+ });
5742
+ const buffer_ = await watermarkifyScreenshotBuffer(rawBuffer, watermarkifyMeta);
5894
5743
  return buffer_.toString("base64");
5895
5744
  } finally {
5896
- await cleanupScreenshotWatermarkify();
5897
5745
  if (restore) {
5898
5746
  await page.evaluate(() => {
5899
5747
  document.querySelectorAll(".__pk_expanded__").forEach((el) => {