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