@insitue/sdk 0.1.4 → 0.1.6

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.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  mountCaptureOnly
3
- } from "./chunk-TTH4NBFA.js";
4
- import "./chunk-65BGOX2M.js";
3
+ } from "./chunk-UWKF2IKW.js";
4
+ import "./chunk-TQEOC7QZ.js";
5
5
  export {
6
6
  mountCaptureOnly
7
7
  };
@@ -1644,22 +1644,33 @@ async function toCanvas(node, options = {}) {
1644
1644
  var IMAGE_PLACEHOLDER = "data:image/svg+xml;base64," + (typeof btoa !== "undefined" ? btoa(
1645
1645
  '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><rect width="32" height="32" fill="#e8e8e8"/><path d="M0 0 L32 32 M32 0 L0 32" stroke="#b0b0b0" stroke-width="1.5"/></svg>'
1646
1646
  ) : "");
1647
- async function preResolveImages() {
1647
+ async function preResolveImages(cropRect) {
1648
1648
  const restorations = [];
1649
1649
  const failedImages = /* @__PURE__ */ new Set();
1650
+ const PAD = 64;
1650
1651
  const images = Array.from(
1651
1652
  document.querySelectorAll("img")
1652
- ).filter(
1653
- (img) => !img.closest?.("#insitu-root, [data-insitu-layer]")
1654
- );
1655
- await Promise.all(
1653
+ ).filter((img) => {
1654
+ if (img.closest?.("#insitu-root, [data-insitu-layer]")) return false;
1655
+ if (!cropRect) return true;
1656
+ const r3 = img.getBoundingClientRect();
1657
+ if (r3.width <= 0 || r3.height <= 0) return false;
1658
+ return r3.right >= cropRect.x - PAD && r3.left <= cropRect.x + cropRect.width + PAD && r3.bottom >= cropRect.y - PAD && r3.top <= cropRect.y + cropRect.height + PAD;
1659
+ });
1660
+ const PER_IMAGE_TIMEOUT_MS = 3e3;
1661
+ await Promise.allSettled(
1656
1662
  images.map(async (img) => {
1657
1663
  const srcToFetch = img.currentSrc || img.src;
1658
1664
  if (!srcToFetch || srcToFetch.startsWith("data:") || srcToFetch.startsWith("blob:")) {
1659
1665
  return;
1660
1666
  }
1667
+ const ac = new AbortController();
1668
+ const timer = setTimeout(() => ac.abort(), PER_IMAGE_TIMEOUT_MS);
1661
1669
  try {
1662
- const res = await fetch(srcToFetch, { cache: "force-cache" });
1670
+ const res = await fetch(srcToFetch, {
1671
+ cache: "force-cache",
1672
+ signal: ac.signal
1673
+ });
1663
1674
  if (!res.ok) {
1664
1675
  failedImages.add(img);
1665
1676
  return;
@@ -1687,6 +1698,8 @@ async function preResolveImages() {
1687
1698
  }
1688
1699
  } catch {
1689
1700
  failedImages.add(img);
1701
+ } finally {
1702
+ clearTimeout(timer);
1690
1703
  }
1691
1704
  })
1692
1705
  );
@@ -1718,7 +1731,7 @@ async function renderViewportCrop(cropRect, pixelRatio) {
1718
1731
  const bodyBg = getComputedStyle(document.body).backgroundColor;
1719
1732
  const htmlBg = getComputedStyle(document.documentElement).backgroundColor;
1720
1733
  const backgroundColor = bodyBg && bodyBg !== "rgba(0, 0, 0, 0)" && bodyBg !== "transparent" ? bodyBg : htmlBg && htmlBg !== "rgba(0, 0, 0, 0)" && htmlBg !== "transparent" ? htmlBg : "#ffffff";
1721
- const { restore: restoreImages, failedImages } = await preResolveImages();
1734
+ const { restore: restoreImages, failedImages } = await preResolveImages(cropRect);
1722
1735
  try {
1723
1736
  const fullCanvas = await toCanvas(document.documentElement, {
1724
1737
  pixelRatio,
@@ -1750,11 +1763,59 @@ async function renderViewportCrop(cropRect, pixelRatio) {
1750
1763
  if (looksBlankUniform(ctx, out.width, out.height)) {
1751
1764
  return { dataUrl: null, failedImages };
1752
1765
  }
1766
+ detectUnrenderedImages(ctx, cropRect, out, pixelRatio, failedImages);
1753
1767
  return { dataUrl: out.toDataURL("image/png"), failedImages };
1754
1768
  } finally {
1755
1769
  restoreImages();
1756
1770
  }
1757
1771
  }
1772
+ function detectUnrenderedImages(cropCtx, cropRect, cropCanvas, pixelRatio, failedImages) {
1773
+ const imgs = Array.from(
1774
+ document.querySelectorAll("img")
1775
+ ).filter(
1776
+ (img) => !img.closest?.("#insitu-root, [data-insitu-layer]")
1777
+ );
1778
+ for (const img of imgs) {
1779
+ if (failedImages.has(img)) continue;
1780
+ const r3 = img.getBoundingClientRect();
1781
+ if (r3.width < 32 || r3.height < 32) continue;
1782
+ const overlapX = Math.min(r3.right, cropRect.x + cropRect.width) - Math.max(r3.left, cropRect.x);
1783
+ const overlapY = Math.min(r3.bottom, cropRect.y + cropRect.height) - Math.max(r3.top, cropRect.y);
1784
+ if (overlapX <= 0 || overlapY <= 0) continue;
1785
+ const baseX = Math.max(r3.left, cropRect.x);
1786
+ const baseY = Math.max(r3.top, cropRect.y);
1787
+ const samples = /* @__PURE__ */ new Set();
1788
+ try {
1789
+ for (let i3 = 0; i3 < 3; i3++) {
1790
+ for (let j3 = 0; j3 < 3; j3++) {
1791
+ const px2 = baseX + overlapX * (i3 + 0.5) / 3;
1792
+ const py = baseY + overlapY * (j3 + 0.5) / 3;
1793
+ const cx = Math.max(
1794
+ 0,
1795
+ Math.min(
1796
+ cropCanvas.width - 1,
1797
+ Math.round((px2 - cropRect.x) * pixelRatio)
1798
+ )
1799
+ );
1800
+ const cy = Math.max(
1801
+ 0,
1802
+ Math.min(
1803
+ cropCanvas.height - 1,
1804
+ Math.round((py - cropRect.y) * pixelRatio)
1805
+ )
1806
+ );
1807
+ const d3 = cropCtx.getImageData(cx, cy, 1, 1).data;
1808
+ samples.add(`${d3[0]},${d3[1]},${d3[2]},${d3[3]}`);
1809
+ }
1810
+ }
1811
+ } catch {
1812
+ continue;
1813
+ }
1814
+ if (samples.size === 1) {
1815
+ failedImages.add(img);
1816
+ }
1817
+ }
1818
+ }
1758
1819
  function looksBlankUniform(ctx, w3, h3) {
1759
1820
  if (w3 < 4 || h3 < 4) return false;
1760
1821
  const samples = [];
@@ -1994,7 +2055,7 @@ async function buildBundle(sel) {
1994
2055
  const settings = getCaptureSettings();
1995
2056
  let screenshot;
1996
2057
  let screenshotUnavailable;
1997
- if (el instanceof HTMLElement) {
2058
+ if (el instanceof HTMLElement || el instanceof SVGElement) {
1998
2059
  const context = findContextAncestor(el);
1999
2060
  const cr = context.getBoundingClientRect();
2000
2061
  const cropRect = new DOMRect(
@@ -2084,7 +2145,14 @@ async function buildBundle(sel) {
2084
2145
  computedStyles: el ? curateComputedStyles(el) : {},
2085
2146
  tailwindClasses: el ? extractTailwindClasses(el) : [],
2086
2147
  ...screenshot ? { screenshot } : {},
2087
- ...screenshotUnavailable ? { screenshotUnavailable } : {},
2148
+ // "Never silent" if neither screenshot nor screenshotUnavailable
2149
+ // got set above (an unexpected fallthrough), surface that fact
2150
+ // so the widget never renders a blank where a result should be.
2151
+ // The structured diagnostic below tells future-me exactly which
2152
+ // branch ran, surfaced as `__insitu_capture__.bundle` in dev.
2153
+ ...screenshotUnavailable ? { screenshotUnavailable } : !screenshot ? {
2154
+ screenshotUnavailable: el ? `screenshot path didn't set a result (el=${el.tagName.toLowerCase()}; rasterisable=${el instanceof HTMLElement || el instanceof SVGElement})` : "no element selected"
2155
+ } : {},
2088
2156
  viewport: {
2089
2157
  w: window.innerWidth,
2090
2158
  h: window.innerHeight,
@@ -10,7 +10,7 @@ import {
10
10
  retryDisplayMedia,
11
11
  stopDisplayMedia,
12
12
  y
13
- } from "./chunk-65BGOX2M.js";
13
+ } from "./chunk-TQEOC7QZ.js";
14
14
 
15
15
  // src/capture-only.ts
16
16
  var DEFAULT_INGEST = "https://www.insitue.com/api/v1/capture";
@@ -260,9 +260,11 @@ function CaptureOnlyApp(props) {
260
260
  }) : bundle?.screenshotUnavailable ? k(
261
261
  "div",
262
262
  {
263
- style: `font-size:12px;color:${C.faint};background:${C.surface2};border:1px solid ${C.line};border-radius:10px;padding:10px;margin-bottom:12px`
263
+ style: `font-size:12px;color:${C.faint};background:${C.surface2};border:1px solid ${C.line};border-radius:10px;padding:10px;margin-bottom:12px;word-break:break-word`
264
264
  },
265
- "Screenshot unavailable \u2014 sending the rest."
265
+ // Surface the actual reason inline — helps diagnose
266
+ // when a capture lands empty without expecting it.
267
+ `Screenshot unavailable \u2014 ${bundle.screenshotUnavailable}`
266
268
  ) : null,
267
269
  // Nudge: the screenshot is structurally OK but some content
268
270
  // couldn't be embedded (non-CORS img / video / canvas). Offer
@@ -347,7 +349,14 @@ function CaptureOnlyApp(props) {
347
349
  {
348
350
  style: `display:flex;justify-content:space-between;padding:9px 16px;border-top:1px solid ${C.line};color:${C.faint};font-size:11px`
349
351
  },
350
- [k("span", {}, "\u{1F512} Secrets scrubbed automatically"), k("span", {}, "InSitue")]
352
+ [
353
+ k("span", {}, "\u{1F512} Secrets scrubbed automatically"),
354
+ k(
355
+ "span",
356
+ { title: `@insitue/sdk@${"0.1.6"}` },
357
+ `InSitue \xB7 v${"0.1.6"}`
358
+ )
359
+ ]
351
360
  )
352
361
  ]);
353
362
  }
@@ -15,7 +15,7 @@ import {
15
15
  setCaptureSettings,
16
16
  stopDisplayMedia,
17
17
  y
18
- } from "./chunk-65BGOX2M.js";
18
+ } from "./chunk-TQEOC7QZ.js";
19
19
 
20
20
  // src/client.ts
21
21
  var CompanionClient = class {
package/dist/index.d.ts CHANGED
@@ -33,4 +33,10 @@ interface InSitueCaptureProps {
33
33
  */
34
34
  declare function InSitueCapture({ projectKey, endpoint, onCapture, }: InSitueCaptureProps): null;
35
35
 
36
- export { InSitue, InSitueCapture, type InSitueCaptureProps, type InSitueProps };
36
+ /** Build-time-inlined version of `@insitue/sdk` (from package.json).
37
+ * Exposed so the host app can self-verify which SDK build is loaded
38
+ * — useful in dev when iterating across publishes. Also surfaced in
39
+ * the capture widget footer so a screenshot proves the build. */
40
+ declare const SDK_VERSION: string;
41
+
42
+ export { InSitue, InSitueCapture, type InSitueCaptureProps, type InSitueProps, SDK_VERSION };
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  mountCaptureOnly
3
- } from "./chunk-TTH4NBFA.js";
3
+ } from "./chunk-UWKF2IKW.js";
4
4
  import {
5
5
  mountInSitue
6
- } from "./chunk-7GRJ5SZQ.js";
7
- import "./chunk-65BGOX2M.js";
6
+ } from "./chunk-V2VYHBQC.js";
7
+ import "./chunk-TQEOC7QZ.js";
8
8
 
9
9
  // src/InSitue.tsx
10
10
  import { useEffect } from "react";
@@ -44,9 +44,13 @@ function InSitueCapture({
44
44
  }, [projectKey, endpoint, onCapture]);
45
45
  return null;
46
46
  }
47
+
48
+ // src/index.ts
49
+ var SDK_VERSION = "0.1.6";
47
50
  export {
48
51
  InSitue,
49
52
  InSitueCapture,
53
+ SDK_VERSION,
50
54
  mountCaptureOnly,
51
55
  mountInSitue
52
56
  };
package/dist/overlay.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  mountInSitue
3
- } from "./chunk-7GRJ5SZQ.js";
4
- import "./chunk-65BGOX2M.js";
3
+ } from "./chunk-V2VYHBQC.js";
4
+ import "./chunk-TQEOC7QZ.js";
5
5
  export {
6
6
  mountInSitue
7
7
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@insitue/sdk",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "InSitue capture SDK — drop one snippet into your deployed app; your users point at a bug, InSitue opens a verified pull request.",
5
5
  "license": "MIT",
6
6
  "type": "module",