@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.js CHANGED
@@ -4521,7 +4521,7 @@ var Logger = {
4521
4521
  import delay2 from "delay";
4522
4522
 
4523
4523
  // src/internals/watermarkify.js
4524
- var SCREENSHOT_WATERMARKIFY_HOST_ID = "__pk_screenshot_watermarkify__";
4524
+ import sharp from "sharp";
4525
4525
  var DEFAULT_TIMEZONE_OFFSET = 8;
4526
4526
  var DEFAULT_RESOLVER_TIMEOUT_MS = 180;
4527
4527
  var DEFAULT_IP_LOOKUP_TIMEOUT_MS = 1e4;
@@ -4532,6 +4532,11 @@ var DEFAULT_WATERMARK_CELL_HEIGHT = 330;
4532
4532
  var DEFAULT_STRIP_LOGO_URL = "https://static.heartbitai.com/geo/icon/favicon.png";
4533
4533
  var DEFAULT_IP_LOOKUP_URL = "http://myip.ipip.net";
4534
4534
  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;
4539
+ var DEFAULT_STRIP_LABEL_WIDTH = 56;
4535
4540
  var WEAK_LOCATION_VALUES = /* @__PURE__ */ new Set(["cn", "\u4E2D\u56FD"]);
4536
4541
  var LOCATION_NETWORK_SUFFIX_PATTERNS = [
4537
4542
  /(?:中国)?移动$/i,
@@ -4567,13 +4572,6 @@ var shortenTail = (value, maxLen = 80) => {
4567
4572
  if (!text || text.length <= maxLen) return text;
4568
4573
  return `${text.slice(0, Math.max(0, maxLen - 1)).trimEnd()}\u2026`;
4569
4574
  };
4570
- var shortenMiddle = (value, maxLen = 56, headLen = 32, tailLen = 14) => {
4571
- const text = toInline(value, Math.max(maxLen * 2, maxLen + headLen + tailLen));
4572
- if (!text || text.length <= maxLen) return text;
4573
- const safeHeadLen = Math.max(4, Math.min(headLen, maxLen - 5));
4574
- const safeTailLen = Math.max(4, Math.min(tailLen, maxLen - safeHeadLen - 1));
4575
- return `${text.slice(0, safeHeadLen).trimEnd()}\u2026${text.slice(-safeTailLen).trimStart()}`;
4576
- };
4577
4575
  var padDatePart = (value) => String(value).padStart(2, "0");
4578
4576
  var formatUtcOffsetLabel = (offsetHours = DEFAULT_TIMEZONE_OFFSET) => {
4579
4577
  const sign = offsetHours >= 0 ? "+" : "-";
@@ -4749,8 +4747,8 @@ var resolveWithCustomResolver = async (page, baseMeta, options = {}) => {
4749
4747
  url: baseMeta.url,
4750
4748
  hostname: baseMeta.hostname,
4751
4749
  title: baseMeta.title,
4750
+ prompt: baseMeta.prompt,
4752
4751
  query: baseMeta.query,
4753
- taskId: baseMeta.taskId,
4754
4752
  serverAddr,
4755
4753
  signal: controller?.signal
4756
4754
  })).catch(() => null),
@@ -4886,22 +4884,23 @@ var resolveEnrichment = async (page, baseMeta, options) => {
4886
4884
  }
4887
4885
  return merged;
4888
4886
  };
4889
- var buildWatermarkStamp = ({ taskId, captureTime, ip, location }) => {
4887
+ var buildWatermarkStamp = ({ prompt, captureTime, ip, location }) => {
4890
4888
  const parts = [
4891
- `TaskID ${shortenMiddle(taskId, 56, 24, 16) || "-"}`,
4889
+ `Prompt ${shortenTail(prompt, 40) || "-"}`,
4892
4890
  `Time ${captureTime}`,
4893
4891
  `Loc ${shortenTail(location, 20) || "-"}`,
4894
4892
  `IP ${toInline(ip, 24) || "-"}`
4895
4893
  ];
4896
4894
  return parts.join(" | ");
4897
4895
  };
4898
- var buildStripSegments = ({ taskId, captureTime, ip, location }) => {
4896
+ var buildStripSegments = ({ prompt, captureTime, ip, location }) => {
4897
+ const promptValue = toInline(prompt, 240) || "-";
4899
4898
  return [
4900
4899
  {
4901
- kind: "taskId",
4902
- label: "TaskID",
4903
- value: toInline(taskId, 240) || "-",
4904
- rawValue: toInline(taskId, 240) || "-"
4900
+ kind: "prompt",
4901
+ label: "Prompt",
4902
+ value: promptValue,
4903
+ rawValue: promptValue
4905
4904
  },
4906
4905
  {
4907
4906
  kind: "time",
@@ -4923,14 +4922,58 @@ var buildStripSegments = ({ taskId, captureTime, ip, location }) => {
4923
4922
  }
4924
4923
  ];
4925
4924
  };
4925
+ var escapeXml = (value) => String(value || "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
4926
+ var estimateTextWidth = (value, fontSize = 16) => {
4927
+ const text = String(value || "");
4928
+ let width = 0;
4929
+ for (const char of text) {
4930
+ if (/\s/.test(char)) {
4931
+ width += fontSize * 0.34;
4932
+ } else if (/[\u4e00-\u9fff\u3400-\u4dbf\u3040-\u30ff\uac00-\ud7af]/.test(char)) {
4933
+ width += fontSize * 0.92;
4934
+ } else if (/[A-Z0-9]/.test(char)) {
4935
+ width += fontSize * 0.62;
4936
+ } else {
4937
+ width += fontSize * 0.56;
4938
+ }
4939
+ }
4940
+ return Math.ceil(width);
4941
+ };
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
+ var resolvePromptFields = (options = {}, fallbackTitle = "") => {
4962
+ const query = normalizeText(options.query);
4963
+ const prompt = normalizeText(options.prompt) || query || normalizeText(fallbackTitle) || "\u672A\u63D0\u4F9B Prompt";
4964
+ return {
4965
+ prompt,
4966
+ query: query || prompt
4967
+ };
4968
+ };
4926
4969
  var createBaseWatermarkifyOptions = () => ({
4927
4970
  enabled: true,
4928
4971
  timezoneOffsetHours: DEFAULT_TIMEZONE_OFFSET,
4929
4972
  response: null,
4930
4973
  ip: "",
4931
4974
  location: "",
4975
+ prompt: "",
4932
4976
  query: "",
4933
- taskId: "",
4934
4977
  ipLookup: true,
4935
4978
  ipLookupTimeoutMs: DEFAULT_IP_LOOKUP_TIMEOUT_MS,
4936
4979
  resolver: null,
@@ -4973,8 +5016,8 @@ var normalizeScreenshotWatermarkify = (value) => {
4973
5016
  response: source.response ?? null,
4974
5017
  ip: normalizeText(source.ip),
4975
5018
  location: normalizeText(source.location),
4976
- query: toInline(source.query, 140),
4977
- taskId: toInline(source.taskId, 120),
5019
+ prompt: toInline(source.prompt, 220),
5020
+ query: toInline(source.query, 220),
4978
5021
  ipLookup: source.ipLookup !== false,
4979
5022
  ipLookupTimeoutMs: Number.isFinite(ipLookupTimeoutMsRaw) ? ipLookupTimeoutMsRaw : DEFAULT_IP_LOOKUP_TIMEOUT_MS,
4980
5023
  resolver: typeof source.resolver === "function" ? source.resolver : null,
@@ -4993,16 +5036,16 @@ var resolveScreenshotWatermarkifyMeta = async (page, options = {}) => {
4993
5036
  const url = normalizeText(page?.url?.()) || "about:blank";
4994
5037
  const title = normalizeText(await page.title().catch(() => "")) || "\u672A\u547D\u540D\u9875\u9762";
4995
5038
  const hostname = getHostname(url);
4996
- const query = normalizeText(options.query) || title || "\u672A\u63D0\u4F9B Query";
4997
- const taskId = normalizeText(options.taskId) || "-";
4998
- const captureTime = formatTimestampForUtcOffset(/* @__PURE__ */ new Date(), timezoneOffsetHours);
5039
+ const { prompt, query } = resolvePromptFields(options, title);
5040
+ const capturedAt = options.capturedAt instanceof Date ? options.capturedAt : new Date(options.capturedAt || Date.now());
5041
+ const captureTime = formatTimestampForUtcOffset(capturedAt, timezoneOffsetHours);
4999
5042
  const [enrichment, stripLogoSrc] = await Promise.all([
5000
5043
  resolveEnrichment(page, {
5001
5044
  url,
5002
5045
  hostname,
5003
5046
  title,
5004
- query,
5005
- taskId
5047
+ prompt,
5048
+ query
5006
5049
  }, options),
5007
5050
  resolveStripLogoSrc()
5008
5051
  ]);
@@ -5010,13 +5053,13 @@ var resolveScreenshotWatermarkifyMeta = async (page, options = {}) => {
5010
5053
  const location = enrichment.location || "-";
5011
5054
  return {
5012
5055
  watermarkText: buildWatermarkStamp({
5013
- taskId,
5056
+ prompt,
5014
5057
  captureTime,
5015
5058
  ip,
5016
5059
  location
5017
5060
  }),
5018
5061
  stripSegments: buildStripSegments({
5019
- taskId,
5062
+ prompt,
5020
5063
  captureTime,
5021
5064
  ip,
5022
5065
  location
@@ -5026,395 +5069,199 @@ var resolveScreenshotWatermarkifyMeta = async (page, options = {}) => {
5026
5069
  stripLogoSrc
5027
5070
  };
5028
5071
  };
5029
- var installScreenshotWatermarkify = async (page, meta) => {
5072
+ var buildFontFamily = () => 'MiSans, "SF Pro Display", "PingFang SC", "Helvetica Neue", Arial, sans-serif';
5073
+ var buildStripSegmentLayouts = (segments, contentWidth) => {
5074
+ 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
5082
+ };
5083
+ const maxValueWidths = {
5084
+ time: 290,
5085
+ location: 220,
5086
+ ip: 160
5087
+ };
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
+ };
5102
+ });
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)
5108
+ );
5109
+ const promptLayout = promptSegment ? {
5110
+ ...promptSegment,
5111
+ fontSize: promptFontSize,
5112
+ labelWidth,
5113
+ renderValue: fitTextToWidth(promptSegment.rawValue || promptSegment.value || "-", promptValueWidth, promptFontSize),
5114
+ segmentWidth: labelWidth + promptValueWidth
5115
+ } : null;
5116
+ return [
5117
+ ...promptLayout ? [promptLayout] : [],
5118
+ ...fixedLayouts
5119
+ ];
5120
+ };
5121
+ var buildWatermarkifySvg = (meta, imageWidth, imageHeight) => {
5030
5122
  const hasWatermark = meta?.watermark?.enabled !== false && normalizeText(meta?.watermarkText);
5031
5123
  const hasStrip = meta?.strip?.enabled !== false && Array.isArray(meta?.stripSegments) && meta.stripSegments.length > 0;
5032
- if (!page || !meta || !hasWatermark && !hasStrip) {
5033
- return async () => {
5034
- };
5124
+ if (!hasWatermark && !hasStrip) {
5125
+ return "";
5035
5126
  }
5036
- await page.evaluate(({ hostId, watermarkifyMeta, defaults }) => {
5037
- const previousHost = document.getElementById(hostId);
5038
- if (previousHost && typeof previousHost.__pkCleanup === "function") {
5039
- previousHost.__pkCleanup();
5040
- }
5041
- previousHost?.remove();
5042
- const mountPoint = document.body || document.documentElement;
5043
- if (!mountPoint) return;
5044
- const createNode = (tagName, className) => {
5045
- const node = document.createElement(tagName);
5046
- if (className) node.className = className;
5047
- return node;
5048
- };
5049
- const safeText = (value) => String(value || "").trim();
5050
- const host = document.createElement("div");
5051
- host.id = hostId;
5052
- host.style.position = "fixed";
5053
- host.style.inset = "0";
5054
- host.style.width = "100vw";
5055
- host.style.height = "100vh";
5056
- host.style.pointerEvents = "none";
5057
- host.style.zIndex = "2147483646";
5058
- const shadow = host.attachShadow({ mode: "open" });
5059
- const style = document.createElement("style");
5060
- style.textContent = `
5061
- :host {
5062
- all: initial;
5063
- position: fixed;
5064
- inset: 0;
5065
- width: 100vw;
5066
- height: 100vh;
5067
- pointer-events: none;
5068
- z-index: 2147483646;
5069
- }
5070
-
5071
- .root {
5072
- position: absolute;
5073
- inset: 0;
5074
- width: 100%;
5075
- height: 100%;
5076
- overflow: hidden;
5077
- pointer-events: none;
5078
- font-family: MiSans, "SF Pro Display", "PingFang SC", "Helvetica Neue", Arial, sans-serif;
5079
- }
5080
-
5081
- .watermark {
5082
- position: absolute;
5083
- inset: 0;
5084
- overflow: hidden;
5085
- pointer-events: none;
5086
- }
5087
-
5088
- .wmStamp {
5089
- position: absolute;
5090
- left: 0;
5091
- top: 0;
5092
- transform-origin: left center;
5093
- pointer-events: none;
5094
- white-space: nowrap;
5095
- }
5096
-
5097
- .wmStampText {
5098
- color: rgba(17, 17, 17, var(--wm-opacity, 0.15));
5099
- font-size: 19px;
5100
- font-weight: 720;
5101
- line-height: 1;
5102
- letter-spacing: 0.032em;
5103
- text-shadow: 0 1px 0 rgba(255, 255, 255, 0.44);
5104
- -webkit-text-stroke: 0.22px rgba(255, 255, 255, 0.12);
5105
- font-variant-numeric: tabular-nums;
5106
- filter: saturate(0.92);
5107
- }
5108
-
5109
- .strip {
5110
- position: absolute;
5111
- left: 0;
5112
- right: 0;
5113
- bottom: 0;
5114
- min-height: 78px;
5115
- padding: 0 22px 0 20px;
5116
- display: flex;
5117
- align-items: center;
5118
- overflow: hidden;
5119
- border-top: 1px solid rgba(17, 17, 17, 0.1);
5120
- background:
5121
- linear-gradient(180deg, rgba(255, 255, 255, 0.995) 0%, rgba(248, 248, 247, 0.985) 100%);
5122
- box-shadow:
5123
- 0 -22px 48px rgba(15, 23, 42, 0.1),
5124
- inset 0 1px 0 rgba(255, 255, 255, 0.92);
5125
- backdrop-filter: blur(16px);
5126
- -webkit-backdrop-filter: blur(16px);
5127
- }
5128
-
5129
- .strip::before {
5130
- content: "";
5131
- position: absolute;
5132
- left: 20px;
5133
- right: 52%;
5134
- top: 0;
5135
- height: 2px;
5136
- border-radius: 999px;
5137
- background: linear-gradient(
5138
- 90deg,
5139
- rgba(17, 17, 17, 0.98) 0%,
5140
- rgba(17, 17, 17, 0.68) 24%,
5141
- rgba(17, 17, 17, 0.08) 58%,
5142
- rgba(17, 17, 17, 0) 100%
5143
- );
5144
- }
5145
-
5146
- .stripInner {
5147
- position: relative;
5148
- z-index: 1;
5149
- display: flex;
5150
- align-items: center;
5151
- gap: 16px;
5152
- width: 100%;
5153
- min-width: 0;
5154
- }
5155
-
5156
- .stripLogo {
5157
- position: relative;
5158
- width: 28px;
5159
- height: 28px;
5160
- flex: none;
5161
- display: flex;
5162
- align-items: center;
5163
- justify-content: center;
5164
- }
5165
-
5166
- .stripLogoImg {
5167
- width: 28px;
5168
- height: 28px;
5169
- object-fit: contain;
5170
- display: block;
5171
- }
5172
-
5173
- .segments {
5174
- display: flex;
5175
- align-items: center;
5176
- gap: 0;
5177
- flex: 1 1 auto;
5178
- min-width: 0;
5179
- white-space: nowrap;
5180
- }
5181
-
5182
- .segment {
5183
- position: relative;
5184
- display: inline-flex;
5185
- align-items: center;
5186
- gap: 10px;
5187
- min-width: 0;
5188
- flex: none;
5189
- padding-right: 16px;
5190
- margin-right: 16px;
5191
- }
5192
-
5193
- .segment:not(:last-child)::after {
5194
- content: "";
5195
- position: absolute;
5196
- top: 50%;
5197
- right: 0;
5198
- width: 1px;
5199
- height: 25px;
5200
- transform: translateY(-50%);
5201
- background: linear-gradient(
5202
- 180deg,
5203
- rgba(148, 163, 184, 0) 0%,
5204
- rgba(148, 163, 184, 0.34) 24%,
5205
- rgba(148, 163, 184, 0.34) 76%,
5206
- rgba(148, 163, 184, 0) 100%
5207
- );
5208
- }
5209
-
5210
- .segmentTaskId {
5211
- flex: 1 1 auto;
5212
- max-width: none;
5213
- min-width: 0;
5214
- }
5215
-
5216
- .label {
5217
- width: 52px;
5218
- color: rgba(17, 17, 17, 0.42);
5219
- font-size: 12px;
5220
- font-weight: 600;
5221
- line-height: 1;
5222
- letter-spacing: 0.01em;
5223
- flex: none;
5224
- display: inline-flex;
5225
- align-items: center;
5226
- justify-content: flex-start;
5227
- }
5228
-
5229
- .value {
5230
- color: #111111;
5231
- font-size: 18px;
5232
- font-weight: 710;
5233
- line-height: 1;
5234
- min-width: 0;
5235
- overflow: hidden;
5236
- text-overflow: ellipsis;
5237
- font-variant-numeric: tabular-nums;
5238
- display: inline-flex;
5239
- align-items: center;
5240
- }
5241
-
5242
- .segmentTaskId .value {
5243
- color: #101010;
5244
- font-size: 15px;
5245
- font-weight: 760;
5246
- letter-spacing: -0.01em;
5247
- }
5248
-
5249
- .segmentTime .value {
5250
- color: #202020;
5251
- font-size: 17px;
5252
- font-weight: 700;
5253
- }
5254
-
5255
- .segmentLocation .value,
5256
- .segmentIp .value {
5257
- color: #2a2a2a;
5258
- font-size: 17px;
5259
- font-weight: 700;
5260
- }
5261
-
5262
- `;
5263
- const root = createNode("div", "root");
5264
- const watermarkNode = createNode("div", "watermark");
5265
- const stripNode = createNode("div", "strip");
5266
- const stripInner = createNode("div", "stripInner");
5267
- const stripLogo = createNode("div", "stripLogo");
5268
- const stripLogoImg = createNode("img", "stripLogoImg");
5269
- const stripSegmentsNode = createNode("div", "segments");
5270
- stripLogoImg.alt = "";
5271
- stripLogoImg.decoding = "async";
5272
- stripLogoImg.referrerPolicy = "no-referrer";
5273
- stripLogoImg.src = safeText(watermarkifyMeta?.stripLogoSrc) || defaults.logoUrl;
5274
- stripLogo.appendChild(stripLogoImg);
5275
- stripInner.appendChild(stripLogo);
5276
- stripInner.appendChild(stripSegmentsNode);
5277
- stripNode.appendChild(stripInner);
5278
- root.appendChild(watermarkNode);
5279
- root.appendChild(stripNode);
5280
- shadow.appendChild(style);
5281
- shadow.appendChild(root);
5282
- mountPoint.appendChild(host);
5283
- const buildSegmentNode = (segment) => {
5284
- const kind = String(segment?.kind || "").trim();
5285
- const label = String(segment?.label || "").trim();
5286
- const value = String(segment?.value || "").trim();
5287
- const rawValue = String(segment?.rawValue || value).trim();
5288
- const classSuffix = kind ? ` segment${kind.slice(0, 1).toUpperCase()}${kind.slice(1)}` : "";
5289
- const segmentNode = createNode("span", `segment${classSuffix}`);
5290
- const labelNode = createNode("span", "label");
5291
- const valueNode = createNode("span", "value");
5292
- labelNode.textContent = label;
5293
- valueNode.textContent = value;
5294
- segmentNode.title = rawValue ? `${label} ${rawValue}` : label;
5295
- segmentNode.appendChild(labelNode);
5296
- segmentNode.appendChild(valueNode);
5297
- return segmentNode;
5298
- };
5299
- const renderStrip = () => {
5300
- const stripSegments = Array.isArray(watermarkifyMeta?.stripSegments) ? watermarkifyMeta.stripSegments : [];
5301
- if (!watermarkifyMeta?.strip?.enabled || stripSegments.length === 0) {
5302
- stripNode.remove();
5303
- return;
5304
- }
5305
- const fragment = document.createDocumentFragment();
5306
- for (const segment of stripSegments) {
5307
- fragment.appendChild(buildSegmentNode(segment));
5308
- }
5309
- stripSegmentsNode.replaceChildren(fragment);
5310
- };
5311
- const waitForLogo = async () => {
5312
- if (!stripLogoImg.getAttribute("src")) {
5313
- return;
5314
- }
5315
- await new Promise((resolve) => {
5316
- let settled = false;
5317
- const done = () => {
5318
- if (settled) return;
5319
- settled = true;
5320
- resolve();
5321
- };
5322
- const fail = () => {
5323
- if (settled) return;
5324
- stripLogo.remove();
5325
- done();
5326
- };
5327
- const timeoutDone = () => {
5328
- if (settled) return;
5329
- done();
5330
- };
5331
- if (stripLogoImg.complete && stripLogoImg.naturalWidth > 0) {
5332
- done();
5333
- return;
5334
- }
5335
- if (stripLogoImg.complete && stripLogoImg.naturalWidth === 0) {
5336
- fail();
5337
- return;
5338
- }
5339
- stripLogoImg.addEventListener("load", done, { once: true });
5340
- stripLogoImg.addEventListener("error", fail, { once: true });
5341
- setTimeout(timeoutDone, 2500);
5342
- });
5343
- };
5344
- const renderWatermark = () => {
5345
- const stampText = safeText(watermarkifyMeta?.watermarkText);
5346
- if (!watermarkifyMeta?.watermark?.enabled || !stampText) {
5347
- watermarkNode.remove();
5348
- return;
5349
- }
5350
- const cellWidth = Math.max(680, Number(watermarkifyMeta.watermark.cellWidth) || defaults.cellWidth);
5351
- const cellHeight = Math.max(260, Number(watermarkifyMeta.watermark.cellHeight) || defaults.cellHeight);
5352
- const rotateDeg = Number(watermarkifyMeta.watermark.rotateDeg) || defaults.rotateDeg;
5353
- const opacity = Math.min(0.22, Math.max(0.05, Number(watermarkifyMeta.watermark.opacity) || defaults.opacity));
5354
- const width = watermarkNode.clientWidth || window.innerWidth || document.documentElement.clientWidth || cellWidth;
5355
- const height = watermarkNode.clientHeight || window.innerHeight || document.documentElement.clientHeight || cellHeight;
5356
- const rowOffset = Math.round(cellWidth * 0.24);
5357
- const startX = -Math.round(cellWidth * 0.16);
5358
- const startY = -Math.round(cellHeight * 0.12);
5359
- const cols = Math.ceil((width + cellWidth * 1.1) / cellWidth) + 1;
5360
- const rows = Math.ceil((height + cellHeight * 0.9) / cellHeight) + 1;
5361
- const fragment = document.createDocumentFragment();
5362
- for (let row = 0; row < rows; row += 1) {
5363
- for (let col = 0; col < cols; col += 1) {
5364
- const stamp = createNode("div", "wmStamp");
5365
- const stampTextNode = createNode("div", "wmStampText");
5366
- const x = startX + col * cellWidth + (row % 2 ? rowOffset : 0);
5367
- const y = startY + row * cellHeight + (row % 2 ? 14 : -8);
5368
- stamp.style.transform = `translate(${x}px, ${y}px) rotate(${rotateDeg}deg)`;
5369
- stampTextNode.style.setProperty("--wm-opacity", String(opacity));
5370
- stampTextNode.textContent = stampText;
5371
- stamp.appendChild(stampTextNode);
5372
- fragment.appendChild(stamp);
5373
- }
5127
+ const width = Math.max(1, Number(imageWidth) || 1);
5128
+ const height = Math.max(1, Number(imageHeight) || 1);
5129
+ const fontFamily = escapeXml(buildFontFamily());
5130
+ const parts = [];
5131
+ if (hasWatermark) {
5132
+ const stampText = escapeXml(normalizeText(meta.watermarkText));
5133
+ const cellWidth = Math.max(680, Number(meta.watermark?.cellWidth) || DEFAULT_WATERMARK_CELL_WIDTH);
5134
+ const cellHeight = Math.max(260, Number(meta.watermark?.cellHeight) || DEFAULT_WATERMARK_CELL_HEIGHT);
5135
+ const rotateDeg = Number(meta.watermark?.rotateDeg) || DEFAULT_WATERMARK_ROTATE_DEG;
5136
+ const opacity = Math.min(0.22, Math.max(0.05, Number(meta.watermark?.opacity) || DEFAULT_WATERMARK_OPACITY));
5137
+ const rowOffset = Math.round(cellWidth * 0.24);
5138
+ const startX = -Math.round(cellWidth * 0.16);
5139
+ const startY = -Math.round(cellHeight * 0.12);
5140
+ const cols = Math.ceil((width + cellWidth * 1.1) / cellWidth) + 1;
5141
+ const rows = Math.ceil((height + cellHeight * 0.9) / cellHeight) + 1;
5142
+ const stamps = [];
5143
+ for (let row = 0; row < rows; row += 1) {
5144
+ for (let col = 0; col < cols; col += 1) {
5145
+ const x = startX + col * cellWidth + (row % 2 ? rowOffset : 0);
5146
+ const y = startY + row * cellHeight + (row % 2 ? 14 : -8);
5147
+ stamps.push(`
5148
+ <g transform="translate(${x} ${y}) rotate(${rotateDeg})">
5149
+ <text
5150
+ x="0"
5151
+ y="0"
5152
+ fill="#111111"
5153
+ fill-opacity="${opacity}"
5154
+ stroke="#ffffff"
5155
+ stroke-opacity="0.12"
5156
+ stroke-width="0.22"
5157
+ font-family="${fontFamily}"
5158
+ font-size="19"
5159
+ font-weight="720"
5160
+ letter-spacing="0.6"
5161
+ >${stampText}</text>
5162
+ </g>
5163
+ `);
5374
5164
  }
5375
- watermarkNode.replaceChildren(fragment);
5376
- };
5377
- let resizeFrame = 0;
5378
- const queueWatermarkRender = () => {
5379
- if (!watermarkNode.isConnected) return;
5380
- cancelAnimationFrame(resizeFrame);
5381
- resizeFrame = requestAnimationFrame(() => {
5382
- renderWatermark();
5383
- });
5384
- };
5385
- const handleResize = () => {
5386
- queueWatermarkRender();
5387
- };
5388
- host.__pkCleanup = () => {
5389
- cancelAnimationFrame(resizeFrame);
5390
- window.removeEventListener("resize", handleResize);
5391
- };
5392
- renderStrip();
5393
- renderWatermark();
5394
- queueWatermarkRender();
5395
- window.addEventListener("resize", handleResize, { passive: true });
5396
- return waitForLogo();
5397
- }, {
5398
- hostId: SCREENSHOT_WATERMARKIFY_HOST_ID,
5399
- watermarkifyMeta: meta,
5400
- defaults: {
5401
- opacity: DEFAULT_WATERMARK_OPACITY,
5402
- rotateDeg: DEFAULT_WATERMARK_ROTATE_DEG,
5403
- cellWidth: DEFAULT_WATERMARK_CELL_WIDTH,
5404
- cellHeight: DEFAULT_WATERMARK_CELL_HEIGHT,
5405
- logoUrl: DEFAULT_STRIP_LOGO_URL
5406
5165
  }
5407
- });
5408
- return async () => {
5409
- await page.evaluate((hostId) => {
5410
- const host = document.getElementById(hostId);
5411
- if (host && typeof host.__pkCleanup === "function") {
5412
- host.__pkCleanup();
5166
+ parts.push(`<g id="watermarks">${stamps.join("")}</g>`);
5167
+ }
5168
+ if (hasStrip) {
5169
+ const stripHeight = DEFAULT_STRIP_HEIGHT;
5170
+ const stripY = height - stripHeight;
5171
+ const logoSize = 28;
5172
+ const logoX = DEFAULT_STRIP_PADDING_LEFT;
5173
+ const logoY = stripY + Math.round((stripHeight - logoSize) / 2);
5174
+ const contentStartX = logoX + logoSize + DEFAULT_STRIP_GAP;
5175
+ const contentWidth = width - contentStartX - DEFAULT_STRIP_PADDING_RIGHT;
5176
+ 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(`
5186
+ <text
5187
+ x="${labelX}"
5188
+ y="${centerY}"
5189
+ fill="#111111"
5190
+ fill-opacity="0.42"
5191
+ font-family="${fontFamily}"
5192
+ font-size="12"
5193
+ font-weight="600"
5194
+ 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>
5205
+ `);
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
+ />
5219
+ `);
5220
+ currentX += DEFAULT_STRIP_GAP + 16;
5413
5221
  }
5414
- host?.remove();
5415
- }, SCREENSHOT_WATERMARKIFY_HOST_ID).catch(() => {
5416
5222
  });
5417
- };
5223
+ parts.push(`
5224
+ <g id="strip">
5225
+ <rect x="0" y="${stripY}" width="${width}" height="${stripHeight}" fill="#ffffff" fill-opacity="0.985" />
5226
+ <rect x="0" y="${stripY}" width="${width}" height="1" fill="#111111" fill-opacity="0.10" />
5227
+ <rect x="${DEFAULT_STRIP_PADDING_LEFT}" y="${stripY}" width="${Math.max(120, Math.round(width * 0.42))}" height="2" fill="url(#stripAccent)" />
5228
+ ${meta.stripLogoSrc ? `<image href="${escapeXml(meta.stripLogoSrc)}" x="${logoX}" y="${logoY}" width="${logoSize}" height="${logoSize}" />` : ""}
5229
+ ${svgSegments.join("")}
5230
+ </g>
5231
+ `);
5232
+ }
5233
+ return `
5234
+ <svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
5235
+ <defs>
5236
+ <linearGradient id="stripAccent" x1="0%" y1="0%" x2="100%" y2="0%">
5237
+ <stop offset="0%" stop-color="#111111" stop-opacity="0.98" />
5238
+ <stop offset="24%" stop-color="#111111" stop-opacity="0.68" />
5239
+ <stop offset="58%" stop-color="#111111" stop-opacity="0.08" />
5240
+ <stop offset="100%" stop-color="#111111" stop-opacity="0" />
5241
+ </linearGradient>
5242
+ </defs>
5243
+ ${parts.join("")}
5244
+ </svg>
5245
+ `;
5246
+ };
5247
+ var watermarkifyScreenshotBuffer = async (buffer, meta) => {
5248
+ const hasWatermark = meta?.watermark?.enabled !== false && normalizeText(meta?.watermarkText);
5249
+ const hasStrip = meta?.strip?.enabled !== false && Array.isArray(meta?.stripSegments) && meta.stripSegments.length > 0;
5250
+ if (!Buffer.isBuffer(buffer) || !meta || !hasWatermark && !hasStrip) {
5251
+ return buffer;
5252
+ }
5253
+ const image = sharp(buffer, { failOn: "none" });
5254
+ const metadata = await image.metadata().catch(() => null);
5255
+ const width = Math.max(1, Number(metadata?.width) || 0);
5256
+ const height = Math.max(1, Number(metadata?.height) || 0);
5257
+ if (!width || !height) {
5258
+ return buffer;
5259
+ }
5260
+ const overlaySvg = buildWatermarkifySvg(meta, width, height);
5261
+ if (!overlaySvg) {
5262
+ return buffer;
5263
+ }
5264
+ return await image.composite([{ input: Buffer.from(overlaySvg), top: 0, left: 0 }]).png().toBuffer();
5418
5265
  };
5419
5266
 
5420
5267
  // src/share.js
@@ -5824,8 +5671,6 @@ var Share = {
5824
5671
  const restore = options.restore ?? false;
5825
5672
  const maxHeight = options.maxHeight ?? 8e3;
5826
5673
  const screenshotWatermarkify = normalizeScreenshotWatermarkify(options.watermarkify ?? true);
5827
- let cleanupScreenshotWatermarkify = async () => {
5828
- };
5829
5674
  try {
5830
5675
  const maxScrollHeight = await page.evaluate(() => {
5831
5676
  let maxHeight2 = document.body.scrollHeight;
@@ -5853,19 +5698,22 @@ var Share = {
5853
5698
  height: targetHeight
5854
5699
  });
5855
5700
  await delay2(1e3);
5856
- if (screenshotWatermarkify.enabled) {
5857
- const watermarkifyMeta = await resolveScreenshotWatermarkifyMeta(page, screenshotWatermarkify);
5858
- cleanupScreenshotWatermarkify = await installScreenshotWatermarkify(page, watermarkifyMeta);
5859
- await delay2(120);
5860
- }
5861
- const buffer_ = await capturePageScreenshot(page, {
5701
+ const capturedAt = /* @__PURE__ */ new Date();
5702
+ const rawBuffer = await capturePageScreenshot(page, {
5862
5703
  fullPage: true,
5863
5704
  type: "png",
5864
5705
  maxClipHeight: targetHeight
5865
5706
  });
5707
+ if (!screenshotWatermarkify.enabled) {
5708
+ return rawBuffer.toString("base64");
5709
+ }
5710
+ const watermarkifyMeta = await resolveScreenshotWatermarkifyMeta(page, {
5711
+ ...screenshotWatermarkify,
5712
+ capturedAt
5713
+ });
5714
+ const buffer_ = await watermarkifyScreenshotBuffer(rawBuffer, watermarkifyMeta);
5866
5715
  return buffer_.toString("base64");
5867
5716
  } finally {
5868
- await cleanupScreenshotWatermarkify();
5869
5717
  if (restore) {
5870
5718
  await page.evaluate(() => {
5871
5719
  document.querySelectorAll(".__pk_expanded__").forEach((el) => {