@insitue/sdk 0.1.3 → 0.1.5
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/capture-only.js +2 -2
- package/dist/{chunk-RYS5Z2BU.js → chunk-2QO63Y4R.js} +117 -57
- package/dist/{chunk-VWPAKOUW.js → chunk-QL6QPM2A.js} +1 -1
- package/dist/{chunk-PRCHVT5A.js → chunk-Z7GTGO4C.js} +13 -4
- package/dist/index.d.ts +7 -1
- package/dist/index.js +7 -3
- package/dist/overlay.js +2 -2
- package/package.json +1 -1
package/dist/capture-only.js
CHANGED
|
@@ -1641,19 +1641,75 @@ async function toCanvas(node, options = {}) {
|
|
|
1641
1641
|
}
|
|
1642
1642
|
|
|
1643
1643
|
// src/capture.ts
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1644
|
+
var IMAGE_PLACEHOLDER = "data:image/svg+xml;base64," + (typeof btoa !== "undefined" ? btoa(
|
|
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
|
+
) : "");
|
|
1647
|
+
async function preResolveImages(cropRect) {
|
|
1648
|
+
const restorations = [];
|
|
1649
|
+
const failedImages = /* @__PURE__ */ new Set();
|
|
1650
|
+
const PAD = 64;
|
|
1651
|
+
const images = Array.from(
|
|
1652
|
+
document.querySelectorAll("img")
|
|
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(
|
|
1662
|
+
images.map(async (img) => {
|
|
1663
|
+
const srcToFetch = img.currentSrc || img.src;
|
|
1664
|
+
if (!srcToFetch || srcToFetch.startsWith("data:") || srcToFetch.startsWith("blob:")) {
|
|
1665
|
+
return;
|
|
1666
|
+
}
|
|
1667
|
+
const ac = new AbortController();
|
|
1668
|
+
const timer = setTimeout(() => ac.abort(), PER_IMAGE_TIMEOUT_MS);
|
|
1669
|
+
try {
|
|
1670
|
+
const res = await fetch(srcToFetch, {
|
|
1671
|
+
cache: "force-cache",
|
|
1672
|
+
signal: ac.signal
|
|
1673
|
+
});
|
|
1674
|
+
if (!res.ok) {
|
|
1675
|
+
failedImages.add(img);
|
|
1676
|
+
return;
|
|
1677
|
+
}
|
|
1678
|
+
const blob = await res.blob();
|
|
1679
|
+
const dataUrl = await new Promise((resolve, reject) => {
|
|
1680
|
+
const reader = new FileReader();
|
|
1681
|
+
reader.onload = () => resolve(reader.result);
|
|
1682
|
+
reader.onerror = () => reject(reader.error);
|
|
1683
|
+
reader.readAsDataURL(blob);
|
|
1684
|
+
});
|
|
1685
|
+
const origSrc = img.getAttribute("src");
|
|
1686
|
+
const origSrcset = img.getAttribute("srcset");
|
|
1687
|
+
restorations.push(() => {
|
|
1688
|
+
if (origSrc != null) img.setAttribute("src", origSrc);
|
|
1689
|
+
else img.removeAttribute("src");
|
|
1690
|
+
if (origSrcset != null) img.setAttribute("srcset", origSrcset);
|
|
1691
|
+
else img.removeAttribute("srcset");
|
|
1692
|
+
});
|
|
1693
|
+
img.removeAttribute("srcset");
|
|
1694
|
+
img.src = dataUrl;
|
|
1695
|
+
try {
|
|
1696
|
+
await img.decode();
|
|
1697
|
+
} catch {
|
|
1698
|
+
}
|
|
1699
|
+
} catch {
|
|
1700
|
+
failedImages.add(img);
|
|
1701
|
+
} finally {
|
|
1702
|
+
clearTimeout(timer);
|
|
1703
|
+
}
|
|
1704
|
+
})
|
|
1705
|
+
);
|
|
1706
|
+
return {
|
|
1707
|
+
restore: () => {
|
|
1708
|
+
for (const r3 of restorations) r3();
|
|
1709
|
+
},
|
|
1710
|
+
failedImages
|
|
1711
|
+
};
|
|
1653
1712
|
}
|
|
1654
|
-
var IMAGE_PLACEHOLDER = "data:image/svg+xml;utf8," + encodeURIComponent(
|
|
1655
|
-
`<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>`
|
|
1656
|
-
);
|
|
1657
1713
|
function findContextAncestor(el) {
|
|
1658
1714
|
const minW = 420;
|
|
1659
1715
|
const minH = 140;
|
|
@@ -1675,37 +1731,42 @@ async function renderViewportCrop(cropRect, pixelRatio) {
|
|
|
1675
1731
|
const bodyBg = getComputedStyle(document.body).backgroundColor;
|
|
1676
1732
|
const htmlBg = getComputedStyle(document.documentElement).backgroundColor;
|
|
1677
1733
|
const backgroundColor = bodyBg && bodyBg !== "rgba(0, 0, 0, 0)" && bodyBg !== "transparent" ? bodyBg : htmlBg && htmlBg !== "rgba(0, 0, 0, 0)" && htmlBg !== "transparent" ? htmlBg : "#ffffff";
|
|
1678
|
-
const
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1734
|
+
const { restore: restoreImages, failedImages } = await preResolveImages(cropRect);
|
|
1735
|
+
try {
|
|
1736
|
+
const fullCanvas = await toCanvas(document.documentElement, {
|
|
1737
|
+
pixelRatio,
|
|
1738
|
+
// cacheBust off — we've already swapped srcs to data URLs.
|
|
1739
|
+
cacheBust: false,
|
|
1740
|
+
backgroundColor,
|
|
1741
|
+
imagePlaceholder: IMAGE_PLACEHOLDER,
|
|
1742
|
+
// Strip only our own overlay layers.
|
|
1743
|
+
filter: (n2) => !(n2 instanceof Element && n2.closest?.("#insitu-root, [data-insitu-layer]"))
|
|
1744
|
+
});
|
|
1745
|
+
const sx = window.scrollX;
|
|
1746
|
+
const sy = window.scrollY;
|
|
1747
|
+
const out = document.createElement("canvas");
|
|
1748
|
+
out.width = Math.max(1, Math.round(cropRect.width * pixelRatio));
|
|
1749
|
+
out.height = Math.max(1, Math.round(cropRect.height * pixelRatio));
|
|
1750
|
+
const ctx = out.getContext("2d");
|
|
1751
|
+
if (!ctx) return { dataUrl: null, failedImages };
|
|
1752
|
+
ctx.drawImage(
|
|
1753
|
+
fullCanvas,
|
|
1754
|
+
Math.round((cropRect.x + sx) * pixelRatio),
|
|
1755
|
+
Math.round((cropRect.y + sy) * pixelRatio),
|
|
1756
|
+
Math.round(cropRect.width * pixelRatio),
|
|
1757
|
+
Math.round(cropRect.height * pixelRatio),
|
|
1758
|
+
0,
|
|
1759
|
+
0,
|
|
1760
|
+
out.width,
|
|
1761
|
+
out.height
|
|
1762
|
+
);
|
|
1763
|
+
if (looksBlankUniform(ctx, out.width, out.height)) {
|
|
1764
|
+
return { dataUrl: null, failedImages };
|
|
1765
|
+
}
|
|
1766
|
+
return { dataUrl: out.toDataURL("image/png"), failedImages };
|
|
1767
|
+
} finally {
|
|
1768
|
+
restoreImages();
|
|
1707
1769
|
}
|
|
1708
|
-
return out.toDataURL("image/png");
|
|
1709
1770
|
}
|
|
1710
1771
|
function looksBlankUniform(ctx, w3, h3) {
|
|
1711
1772
|
if (w3 < 4 || h3 < 4) return false;
|
|
@@ -1724,7 +1785,7 @@ function looksBlankUniform(ctx, w3, h3) {
|
|
|
1724
1785
|
}
|
|
1725
1786
|
return new Set(samples).size === 1;
|
|
1726
1787
|
}
|
|
1727
|
-
function assessCaptureQuality(cropRect) {
|
|
1788
|
+
function assessCaptureQuality(cropRect, failedImages) {
|
|
1728
1789
|
const out = {
|
|
1729
1790
|
unembeddableImages: 0,
|
|
1730
1791
|
hasVideo: false,
|
|
@@ -1739,13 +1800,7 @@ function assessCaptureQuality(cropRect) {
|
|
|
1739
1800
|
const overlaps = r3.right >= cropRect.x && r3.left <= cropRect.x + cropRect.width && r3.bottom >= cropRect.y && r3.top <= cropRect.y + cropRect.height;
|
|
1740
1801
|
if (!overlaps) continue;
|
|
1741
1802
|
if (el instanceof HTMLImageElement) {
|
|
1742
|
-
|
|
1743
|
-
if (!src) continue;
|
|
1744
|
-
const browserLoaded = el.complete && el.naturalWidth > 0;
|
|
1745
|
-
const corsSafe = !crossOrigin(src) || el.crossOrigin != null;
|
|
1746
|
-
if (!browserLoaded || !corsSafe) {
|
|
1747
|
-
out.unembeddableImages++;
|
|
1748
|
-
}
|
|
1803
|
+
if (failedImages.has(el)) out.unembeddableImages++;
|
|
1749
1804
|
} else if (el instanceof HTMLVideoElement) {
|
|
1750
1805
|
if (r3.width > 0 && r3.height > 0) out.hasVideo = true;
|
|
1751
1806
|
} else if (el instanceof HTMLCanvasElement) {
|
|
@@ -1952,7 +2007,7 @@ async function buildBundle(sel) {
|
|
|
1952
2007
|
const settings = getCaptureSettings();
|
|
1953
2008
|
let screenshot;
|
|
1954
2009
|
let screenshotUnavailable;
|
|
1955
|
-
if (el instanceof HTMLElement) {
|
|
2010
|
+
if (el instanceof HTMLElement || el instanceof SVGElement) {
|
|
1956
2011
|
const context = findContextAncestor(el);
|
|
1957
2012
|
const cr = context.getBoundingClientRect();
|
|
1958
2013
|
const cropRect = new DOMRect(
|
|
@@ -1972,11 +2027,9 @@ async function buildBundle(sel) {
|
|
|
1972
2027
|
let layer1Result = null;
|
|
1973
2028
|
let quality = null;
|
|
1974
2029
|
if (!skipLayer1) {
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
);
|
|
1979
|
-
quality = assessCaptureQuality(cropRect);
|
|
2030
|
+
const r3 = await renderViewportCrop(cropRect, Math.min(dpr, 1.5));
|
|
2031
|
+
layer1Result = r3.dataUrl;
|
|
2032
|
+
quality = assessCaptureQuality(cropRect, r3.failedImages);
|
|
1980
2033
|
}
|
|
1981
2034
|
const imperfect = !layer1Result || quality != null && (quality.unembeddableImages > 0 || quality.hasVideo || quality.hasCanvas);
|
|
1982
2035
|
if (imperfect || skipLayer1) {
|
|
@@ -2044,7 +2097,14 @@ async function buildBundle(sel) {
|
|
|
2044
2097
|
computedStyles: el ? curateComputedStyles(el) : {},
|
|
2045
2098
|
tailwindClasses: el ? extractTailwindClasses(el) : [],
|
|
2046
2099
|
...screenshot ? { screenshot } : {},
|
|
2047
|
-
|
|
2100
|
+
// "Never silent" — if neither screenshot nor screenshotUnavailable
|
|
2101
|
+
// got set above (an unexpected fallthrough), surface that fact
|
|
2102
|
+
// so the widget never renders a blank where a result should be.
|
|
2103
|
+
// The structured diagnostic below tells future-me exactly which
|
|
2104
|
+
// branch ran, surfaced as `__insitu_capture__.bundle` in dev.
|
|
2105
|
+
...screenshotUnavailable ? { screenshotUnavailable } : !screenshot ? {
|
|
2106
|
+
screenshotUnavailable: el ? `screenshot path didn't set a result (el=${el.tagName.toLowerCase()}; rasterisable=${el instanceof HTMLElement || el instanceof SVGElement})` : "no element selected"
|
|
2107
|
+
} : {},
|
|
2048
2108
|
viewport: {
|
|
2049
2109
|
w: window.innerWidth,
|
|
2050
2110
|
h: window.innerHeight,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
retryDisplayMedia,
|
|
11
11
|
stopDisplayMedia,
|
|
12
12
|
y
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-2QO63Y4R.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
|
-
|
|
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
|
-
[
|
|
352
|
+
[
|
|
353
|
+
k("span", {}, "\u{1F512} Secrets scrubbed automatically"),
|
|
354
|
+
k(
|
|
355
|
+
"span",
|
|
356
|
+
{ title: `@insitue/sdk@${"0.1.5"}` },
|
|
357
|
+
`InSitue \xB7 v${"0.1.5"}`
|
|
358
|
+
)
|
|
359
|
+
]
|
|
351
360
|
)
|
|
352
361
|
]);
|
|
353
362
|
}
|
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
|
-
|
|
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-
|
|
3
|
+
} from "./chunk-Z7GTGO4C.js";
|
|
4
4
|
import {
|
|
5
5
|
mountInSitue
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-QL6QPM2A.js";
|
|
7
|
+
import "./chunk-2QO63Y4R.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.5";
|
|
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
package/package.json
CHANGED