@merittdev/horus-lens 0.1.0 → 0.1.1

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/browser.cjs CHANGED
@@ -29353,6 +29353,11 @@ var ReplayRecorder = class {
29353
29353
  emit: (event, isCheckout) => this.onEmit(event, isCheckout),
29354
29354
  checkoutEveryNms: this.windowMs,
29355
29355
  recordCanvas: false,
29356
+ // Inline same-origin + CORS images as data URIs at snapshot time so the
29357
+ // dashboard replay paints on a different origin (tainted cross-origin
29358
+ // images fall back to their URL, which is fine for public CDNs).
29359
+ inlineImages: true,
29360
+ collectFonts: true,
29356
29361
  // Keep the Lens overlay itself out of every recording.
29357
29362
  blockSelector: `#${LENS_OVERLAY_HOST_ID}`
29358
29363
  });
@@ -29459,7 +29464,7 @@ var ReplayRecorder = class {
29459
29464
  console.warn("[lens] rrweb recording unavailable; replay disabled", err);
29460
29465
  }
29461
29466
  };
29462
- async function captureScreenshot(excludeHost) {
29467
+ async function captureScreenshot(excludeHost, crop) {
29463
29468
  if (typeof document === "undefined" || typeof window === "undefined") return null;
29464
29469
  try {
29465
29470
  const viewportPixels = window.innerWidth * window.innerHeight || 1;
@@ -29493,12 +29498,50 @@ async function captureScreenshot(excludeHost) {
29493
29498
  );
29494
29499
  return null;
29495
29500
  }
29501
+ if (crop) {
29502
+ const cropped = await cropBlob(blob2, crop, scale);
29503
+ return cropped ?? blob2;
29504
+ }
29496
29505
  return blob2;
29497
29506
  } catch (err) {
29498
29507
  console.warn("[lens] screenshot capture failed:", err);
29499
29508
  return null;
29500
29509
  }
29501
29510
  }
29511
+ async function cropBlob(blob2, crop, scale) {
29512
+ try {
29513
+ if (typeof createImageBitmap !== "function" || typeof document === "undefined") return null;
29514
+ const bitmap = await createImageBitmap(blob2);
29515
+ let sx = Math.round(crop.x * scale);
29516
+ let sy = Math.round(crop.y * scale);
29517
+ let sw = Math.round(crop.width * scale);
29518
+ let sh = Math.round(crop.height * scale);
29519
+ sx = Math.max(0, Math.min(sx, bitmap.width));
29520
+ sy = Math.max(0, Math.min(sy, bitmap.height));
29521
+ sw = Math.min(sw, bitmap.width - sx);
29522
+ sh = Math.min(sh, bitmap.height - sy);
29523
+ if (sw <= 0 || sh <= 0) {
29524
+ bitmap.close?.();
29525
+ return null;
29526
+ }
29527
+ const canvas = document.createElement("canvas");
29528
+ canvas.width = sw;
29529
+ canvas.height = sh;
29530
+ const ctx = canvas.getContext("2d");
29531
+ if (!ctx) {
29532
+ bitmap.close?.();
29533
+ return null;
29534
+ }
29535
+ ctx.drawImage(bitmap, sx, sy, sw, sh, 0, 0, sw, sh);
29536
+ bitmap.close?.();
29537
+ return await new Promise((resolve) => {
29538
+ canvas.toBlob((out) => resolve(out), "image/jpeg", 0.82);
29539
+ });
29540
+ } catch (err) {
29541
+ console.warn("[lens] screenshot crop failed:", err);
29542
+ return null;
29543
+ }
29544
+ }
29502
29545
  function parseBrowser(ua) {
29503
29546
  if (!ua) return void 0;
29504
29547
  const checks = [
@@ -29763,7 +29806,7 @@ var LensClient = class {
29763
29806
  const wantErrors = include.errors !== false;
29764
29807
  const wantScreenshot = include.screenshot !== false;
29765
29808
  const wantDom = include.dom !== false;
29766
- const screenshotPromise = this.screenshotEnabled && wantScreenshot ? captureScreenshot(input2.excludeFromScreenshot) : Promise.resolve(null);
29809
+ const screenshotPromise = this.screenshotEnabled && wantScreenshot ? captureScreenshot(input2.excludeFromScreenshot, input2.screenshotCrop) : Promise.resolve(null);
29767
29810
  const replayEvents = wantReplay ? this.replay?.snapshot() ?? [] : [];
29768
29811
  const errorEntries = this.errorBuffer.drain();
29769
29812
  const consoleTail = this.consoleBuffer.tail(CONSOLE_TAIL_SIZE);
@@ -29999,6 +30042,13 @@ var Annotator = class {
29999
30042
  list() {
30000
30043
  return this.annotations.map((a) => ({ ...a, viewport: { ...a.viewport } }));
30001
30044
  }
30045
+ /**
30046
+ * Live rendered rect elements (fixed-positioned, viewport coords). Exposed so
30047
+ * the overlay can derive a document-space bounding box for screenshot crop.
30048
+ */
30049
+ rects() {
30050
+ return [...this.renderedRects];
30051
+ }
30002
30052
  clear() {
30003
30053
  this.annotations = [];
30004
30054
  for (const rect of this.renderedRects) rect.remove();
@@ -31276,11 +31326,13 @@ var Overlay = class {
31276
31326
  }
31277
31327
  async submit(comment2, include) {
31278
31328
  const annotations = this.annotator.list();
31329
+ const screenshotCrop = this.computeAnnotationCrop();
31279
31330
  const result2 = await this.deps.client.submitReport({
31280
31331
  comment: comment2 || void 0,
31281
31332
  annotations,
31282
31333
  target: this.target ?? void 0,
31283
31334
  excludeFromScreenshot: this.host,
31335
+ screenshotCrop,
31284
31336
  include
31285
31337
  });
31286
31338
  this.target = null;
@@ -31289,6 +31341,35 @@ var Overlay = class {
31289
31341
  this.deps.client.discardRecording();
31290
31342
  return result2;
31291
31343
  }
31344
+ /**
31345
+ * Union the marked rects into a single bounding box in DOCUMENT coordinates,
31346
+ * or `undefined` if nothing is marked (→ full-page screenshot). Each rect is
31347
+ * fixed-positioned, so getBoundingClientRect() gives viewport coords; add the
31348
+ * page scroll offset to convert to document space.
31349
+ */
31350
+ computeAnnotationCrop() {
31351
+ const rects = this.annotator.rects();
31352
+ if (rects.length === 0) return void 0;
31353
+ const scrollX = window.scrollX || window.pageXOffset || 0;
31354
+ const scrollY = window.scrollY || window.pageYOffset || 0;
31355
+ let minX = Infinity;
31356
+ let minY = Infinity;
31357
+ let maxX = -Infinity;
31358
+ let maxY = -Infinity;
31359
+ for (const rect of rects) {
31360
+ const r = rect.getBoundingClientRect();
31361
+ const left = r.left + scrollX;
31362
+ const top = r.top + scrollY;
31363
+ minX = Math.min(minX, left);
31364
+ minY = Math.min(minY, top);
31365
+ maxX = Math.max(maxX, left + r.width);
31366
+ maxY = Math.max(maxY, top + r.height);
31367
+ }
31368
+ const width = maxX - minX;
31369
+ const height = maxY - minY;
31370
+ if (!(width > 0) || !(height > 0)) return void 0;
31371
+ return { x: minX, y: minY, width, height };
31372
+ }
31292
31373
  dismiss() {
31293
31374
  this.deps.onDismiss();
31294
31375
  this.destroy();