@comty.io/core 0.1.4 → 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/README.md +1 -1
- package/comty-sdk.js +7 -5
- package/package.json +1 -1
- package/src/capture-engine.js +1 -1
- package/src/capture.js +6 -4
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Comty's browser SDK provides shared comments and an element inspector, with no n
|
|
|
6
6
|
|
|
7
7
|
Posting immediately displays a pending comment and sends the JSON request. After the API confirms the save, the SDK captures and uploads the viewport separately. Screenshot failures do not roll back comments; save failures restore the draft for retry. Screenshot annotations reuse the real Comty pin and hover card (avatar, author, date and content). They are measured in an invisible, inert host, then cloned with their computed styles and embedded resources into the screenshot. The temporary host is removed after cloning and never appears on screen.
|
|
8
8
|
|
|
9
|
-
The bundled capture engine is based on `html-to-image` 1.11.13 (MIT), not SnapDOM. It embeds web fonts, responsive images, CSS backgrounds and pseudo-elements, preserves scroll offsets, and yields during DOM traversal. Offscreen leaf boxes keep their layout dimensions without their rendering cost. PNG is preferred for text; large images fall back to JPEG. Captures are capped at 2× resolution / 2560 pixels per side and have a 15-second processing deadline.
|
|
9
|
+
The bundled capture engine is based on `html-to-image` 1.11.13 (MIT), not SnapDOM. It embeds web fonts, responsive images, CSS backgrounds and pseudo-elements, preserves scroll offsets, and yields during DOM traversal. Each font face keeps its source when selecting WOFF2, including consecutive subset and weight rules. Transparent page backgrounds are composited over the browser's light or dark canvas so the image viewer cannot tint the screenshot. Offscreen leaf boxes keep their layout dimensions without their rendering cost. PNG is preferred for text; large images fall back to JPEG. Captures are capped at 2× resolution / 2560 pixels per side and have a 15-second processing deadline.
|
|
10
10
|
|
|
11
11
|
Resources are fetched directly first. Public cross-origin assets blocked by CORS can use `/api/sdk/capture-resource`, authenticated with the SDK token and restricted to the project's allowed domain. The API validates and pins public DNS addresses on every redirect, limits bytes/concurrency/time, and never forwards credentials to resource hosts. Deploy the API alongside the SDK to enable this fallback.
|
|
12
12
|
|
package/comty-sdk.js
CHANGED
|
@@ -1901,7 +1901,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
1901
1901
|
SOFTWARE.
|
|
1902
1902
|
|
|
1903
1903
|
*/
|
|
1904
|
-
function S(t,e){if(t.match(/^[a-z]+:\/\//i))return t;if(t.match(/^\/\//))return window.location.protocol+t;if(t.match(/^[a-z]+:/i))return t;let r=document.implementation.createHTMLDocument(),n=r.createElement("base"),i=r.createElement("a");return r.head.appendChild(n),r.body.appendChild(i),e&&(n.href=e),i.href=t,i.href}var P=(()=>{let t=0,e=()=>`0000${(Math.random()*36**4<<0).toString(36)}`.slice(-4);return()=>(t+=1,`u${e()}${t}`)})();function u(t){let e=[];for(let r=0,n=t.length;r<n;r++)e.push(t[r]);return e}var f=null;function y(t={}){return f||(t.includeStyleProperties?(f=t.includeStyleProperties,f):(f=u(window.getComputedStyle(document.documentElement)),f))}function w(t){return new Promise((e,r)=>{let n=new Image;n.onload=()=>{n.decode().then(()=>{requestAnimationFrame(()=>e(n))})},n.onerror=r,n.crossOrigin="anonymous",n.decoding="async",n.src=t})}async function
|
|
1904
|
+
function S(t,e){if(t.match(/^[a-z]+:\/\//i))return t;if(t.match(/^\/\//))return window.location.protocol+t;if(t.match(/^[a-z]+:/i))return t;let r=document.implementation.createHTMLDocument(),n=r.createElement("base"),i=r.createElement("a");return r.head.appendChild(n),r.body.appendChild(i),e&&(n.href=e),i.href=t,i.href}var P=(()=>{let t=0,e=()=>`0000${(Math.random()*36**4<<0).toString(36)}`.slice(-4);return()=>(t+=1,`u${e()}${t}`)})();function u(t){let e=[];for(let r=0,n=t.length;r<n;r++)e.push(t[r]);return e}var f=null;function y(t={}){return f||(t.includeStyleProperties?(f=t.includeStyleProperties,f):(f=u(window.getComputedStyle(document.documentElement)),f))}function w(t){return new Promise((e,r)=>{let n=new Image;n.onload=()=>{n.decode().then(()=>{requestAnimationFrame(()=>e(n))})},n.onerror=r,n.crossOrigin="anonymous",n.decoding="async",n.src=t})}async function I(t){return Promise.resolve().then(()=>new XMLSerializer().serializeToString(t)).then(encodeURIComponent).then(e=>`data:image/svg+xml;charset=utf-8,${e}`)}async function U(t,e,r){let n="http://www.w3.org/2000/svg",i=document.createElementNS(n,"svg"),o=document.createElementNS(n,"foreignObject");return i.setAttribute("width",`${e}`),i.setAttribute("height",`${r}`),i.setAttribute("viewBox",`0 0 ${e} ${r}`),o.setAttribute("width","100%"),o.setAttribute("height","100%"),o.setAttribute("x","0"),o.setAttribute("y","0"),o.setAttribute("externalResourcesRequired","true"),i.appendChild(o),o.appendChild(t),I(i)}var l=(t,e)=>{if(t instanceof e)return!0;let r=Object.getPrototypeOf(t);return r===null?!1:r.constructor.name===e.name||l(r,e)};function V(t){let e=t.getPropertyValue("content");return`${t.cssText} content: '${e.replace(/'|"/g,"")}';`}function M(t,e){return y(e).map(r=>{let n=t.getPropertyValue(r),i=t.getPropertyPriority(r);return`${r}: ${n}${i?" !important":""};`}).join(" ")}function F(t,e,r,n){let i=`.${t}:${e}`,o=r.cssText?V(r):M(r,n);return document.createTextNode(`${i}{${o}}`)}function R(t,e,r,n){let i=window.getComputedStyle(t,r),o=i.getPropertyValue("content");if(o===""||o==="none")return;let a=P();try{e.className=`${e.className} ${a}`}catch{return}let c=document.createElement("style");c.appendChild(F(a,r,i,n)),e.appendChild(c)}function C(t,e,r){R(t,e,":before",r),R(t,e,":after",r)}var T="application/font-woff",$="image/jpeg",H={woff:T,woff2:T,ttf:"application/font-truetype",eot:"application/vnd.ms-fontobject",png:"image/png",jpg:$,jpeg:$,gif:"image/gif",tiff:"image/tiff",svg:"image/svg+xml",webp:"image/webp"};function O(t){let e=/\.([^./]*?)$/g.exec(t);return e?e[1]:""}function m(t){let e=O(t).toLowerCase();return H[e]||""}function k(t){return t.split(/,/)[1]}function p(t){return t.search(/^(data:)/)!==-1}function x(t,e){return`data:${e};base64,${t}`}async function q(t,e,r){let n=await(e?.captureFetch||fetch)(t,e);if(!n.ok)throw new Error(`Resource "${n.url}" not found`);let i=await n.blob();return new Promise((o,a)=>{let c=new FileReader;c.onerror=a,c.onloadend=()=>{try{o(r({res:n,result:c.result}))}catch(s){a(s)}},c.readAsDataURL(i)})}async function h(t,e,r){let n=r.resourceCache||{},i=t;if(n[i]!=null)return n[i];r.cacheBust&&(t+=(/\?/.test(t)?"&":"?")+new Date().getTime());let o;try{let a=await q(t,r.fetchRequestInit,({res:c,result:s})=>(e||(e=c.headers.get("Content-Type")||""),k(s)));o=x(a,e)}catch(a){o=r.imagePlaceholder||"";let c=`Failed to fetch resource: ${t}`;a&&(c=typeof a=="string"?a:a.message),c&&console.warn(c)}return n[i]=o,o}async function _(t){let e=t.toDataURL();return e==="data:,"?t.cloneNode(!1):w(e)}async function v(t,e){if(t.currentSrc){let o=document.createElement("canvas"),a=o.getContext("2d");o.width=t.clientWidth,o.height=t.clientHeight,a?.drawImage(t,0,0,o.width,o.height);let c=o.toDataURL();return w(c)}let r=t.poster,n=m(r),i=await h(r,n,e);return w(i)}async function B(t,e){var r;try{if(!((r=t?.contentDocument)===null||r===void 0)&&r.body)return await b(t.contentDocument.body,e,!0)}catch{}return t.cloneNode(!1)}async function G(t,e){return l(t,HTMLCanvasElement)?_(t).catch(()=>t.cloneNode(!1)):l(t,HTMLVideoElement)?v(t,e).catch(()=>document.createElement("div")):l(t,HTMLIFrameElement)?B(t,e):t.cloneNode(!1)}var j=t=>t.tagName!=null&&t.tagName.toUpperCase()==="SLOT";async function z(t,e,r){var n,i;let o=[];return j(t)&&t.assignedNodes?o=u(t.assignedNodes()):l(t,HTMLIFrameElement)&&(!((n=t.contentDocument)===null||n===void 0)&&n.body)?o=u(t.contentDocument.body.childNodes):o=u(((i=t.shadowRoot)!==null&&i!==void 0?i:t).childNodes),o.length===0||l(t,HTMLVideoElement)||await o.reduce((a,c)=>a.then(()=>b(c,r)).then(s=>{s&&e.appendChild(s)}),Promise.resolve()),e}function W(t,e,r){let n=e.style;if(!n)return;let i=window.getComputedStyle(t);i.cssText?(n.cssText=i.cssText,n.transformOrigin=i.transformOrigin):y(r).forEach(o=>{let a=i.getPropertyValue(o);o==="font-size"&&a.endsWith("px")&&(a=`${parseFloat(a.substring(0,a.length-2))}px`),l(t,HTMLIFrameElement)&&o==="display"&&a==="inline"&&(a="block"),o==="d"&&e.getAttribute("d")&&(a=`path(${e.getAttribute("d")})`),n.setProperty(o,a,i.getPropertyPriority(o))})}function X(t,e){l(t,HTMLTextAreaElement)&&(e.textContent=t.value),l(t,HTMLInputElement)&&e.setAttribute("value",t.value)}function K(t,e){if(l(t,HTMLSelectElement)){let n=Array.from(e.children).find(i=>t.value===i.getAttribute("value"));n&&n.setAttribute("selected","")}}function J(t,e,r){return l(e,Element)&&(W(t,e,r),C(t,e,r),X(t,e),K(t,e)),e}async function Q(t,e){let r=t.querySelectorAll?t.querySelectorAll("use"):[];if(r.length===0)return t;let n={};for(let o=0;o<r.length;o++){let a=r[o],c=a.getAttribute("href")||a.getAttribute("xlink:href");if(c){let s=t.querySelector(c),d=document.querySelector(c);!s&&d&&!n[c]&&(n[c]=await b(d,e,!0))}}let i=Object.values(n);if(i.length){let o="http://www.w3.org/1999/xhtml",a=document.createElementNS(o,"svg");a.setAttribute("xmlns",o),a.style.position="absolute",a.style.width="0",a.style.height="0",a.style.overflow="hidden",a.style.display="none";let c=document.createElementNS(o,"defs");a.appendChild(c);for(let s=0;s<i.length;s++)c.appendChild(i[s]);t.appendChild(a)}return t}async function b(t,e,r){if(await e.beforeClone?.(t),!r&&e.filter&&!e.filter(t))return null;let n=e.placeholder?.(t);return n||Promise.resolve(t).then(i=>G(i,e)).then(i=>z(t,i,e)).then(async i=>(await e.beforeClone?.(t),J(t,i,e))).then(i=>e.afterClone?.(t,i)??i).then(i=>Q(i,e))}var A=/url\((['"]?)([^'"]+?)\1\)/g,L=/url\([^)]+\)\s*format\((["']?)([^"']+)\1\)/g,Y=/src:\s*(?:url\([^)]+\)\s*format\([^)]+\)[,;]\s*)+/g;function Z(t){let e=t.replace(/([.*+?^${}()|\[\]\/\\])/g,"\\$1");return new RegExp(`(url\\(['"]?)(${e})(['"]?\\))`,"g")}function N(t){let e=[];return t.replace(A,(r,n,i)=>(e.push(i),r)),e.filter(r=>!p(r)&&!r.startsWith("#"))}async function tt(t,e,r,n,i){try{let o=r?S(e,r):e,a=m(e),c;if(i){let s=await i(o);c=x(s,a)}else c=await h(o,a,n);return t.replace(Z(e),`$1${c}$3`)}catch{}return t}function et(t,{preferredFontFormat:e}){return e?t.replace(Y,r=>{for(L.lastIndex=0;;){let[n,,i]=L.exec(r)||[];if(!i)return"";if(i===e)return`src: ${n};`}}):t}function rt(t){return t.search(A)!==-1}async function E(t,e,r){if(!rt(t))return t;let n=et(t,r);return N(n).reduce((o,a)=>o.then(c=>tt(c,a,e,r)),Promise.resolve(n))}async function g(t,e,r){var n;let i=(n=e.style)===null||n===void 0?void 0:n.getPropertyValue(t);if(i){let o=await E(i,null,r);return e.style.setProperty(t,o,e.style.getPropertyPriority(t)),!0}return!1}async function nt(t,e){await g("background",t,e)||await g("background-image",t,e),await g("mask",t,e)||await g("-webkit-mask",t,e)||await g("mask-image",t,e)||await g("-webkit-mask-image",t,e)}async function it(t,e){let r=l(t,HTMLImageElement);if(!(r&&!p(t.src))&&!(l(t,SVGImageElement)&&!p(t.href.baseVal)))return;let n=r?t.src:t.href.baseVal,i=await h(n,m(n),e);await new Promise((o,a)=>{t.onload=o,t.onerror=e.onImageErrorHandler?(...s)=>{try{o(e.onImageErrorHandler(...s))}catch(d){a(d)}}:a;let c=t;c.decode&&(c.decode=o),c.loading==="lazy"&&(c.loading="eager"),r?(t.srcset="",t.src=i):t.href.baseVal=i})}async function ot(t,e){let n=u(t.childNodes).map(i=>D(i,e));await Promise.all(n).then(()=>t)}async function D(t,e){await e.beforeClone?.(t),l(t,Element)&&(await nt(t,e),await it(t,e),await ot(t,e))} return {cloneNode:b,embedImages:D,embedResources:E,nodeToDataURL:U}; })();
|
|
1905
1905
|
// Fetches belong to one capture. Never forward SDK credentials to a resource host.
|
|
1906
1906
|
function createCaptureResources({ apiUrl, projectKey, getHeaders, signal }) {
|
|
1907
1907
|
const cache = new Map();
|
|
@@ -2221,7 +2221,12 @@ function createScreenshotCapture({ apiUrl, projectKey, getHeaders = () => ({}) }
|
|
|
2221
2221
|
await untilAborted(captureEngine.embedImages(stage, options));
|
|
2222
2222
|
checkContext();
|
|
2223
2223
|
await yieldCapture();
|
|
2224
|
-
|
|
2224
|
+
// Transparent page backgrounds reveal the browser canvas, not the image viewer.
|
|
2225
|
+
const viewport = document.createElement("div");
|
|
2226
|
+
viewport.style.backgroundColor = "Canvas";
|
|
2227
|
+
viewport.style.colorScheme = rootStyle.colorScheme;
|
|
2228
|
+
viewport.append(stage);
|
|
2229
|
+
const svg = await captureEngine.nodeToDataURL(viewport, context.width, context.height);
|
|
2225
2230
|
const image = new Image();
|
|
2226
2231
|
image.src = svg;
|
|
2227
2232
|
await untilAborted(image.decode());
|
|
@@ -2236,9 +2241,6 @@ function createScreenshotCapture({ apiUrl, projectKey, getHeaders = () => ({}) }
|
|
|
2236
2241
|
let blob = await untilAborted(new Promise(resolve => canvas.toBlob(resolve, "image/png")));
|
|
2237
2242
|
if (!blob) throw new Error("Screenshot encoding failed");
|
|
2238
2243
|
if (blob.size > 2800000) {
|
|
2239
|
-
ctx.globalCompositeOperation = "destination-over";
|
|
2240
|
-
ctx.fillStyle = getComputedStyle(document.documentElement).getPropertyValue("background-color");
|
|
2241
|
-
ctx.fillRect(0, 0, context.width, context.height);
|
|
2242
2244
|
blob = await untilAborted(new Promise(resolve => canvas.toBlob(resolve, "image/jpeg", 0.9)));
|
|
2243
2245
|
if (!blob) throw new Error("Screenshot encoding failed");
|
|
2244
2246
|
}
|
package/package.json
CHANGED
package/src/capture-engine.js
CHANGED
|
@@ -23,4 +23,4 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
23
23
|
SOFTWARE.
|
|
24
24
|
|
|
25
25
|
*/
|
|
26
|
-
function S(t,e){if(t.match(/^[a-z]+:\/\//i))return t;if(t.match(/^\/\//))return window.location.protocol+t;if(t.match(/^[a-z]+:/i))return t;let r=document.implementation.createHTMLDocument(),n=r.createElement("base"),i=r.createElement("a");return r.head.appendChild(n),r.body.appendChild(i),e&&(n.href=e),i.href=t,i.href}var P=(()=>{let t=0,e=()=>`0000${(Math.random()*36**4<<0).toString(36)}`.slice(-4);return()=>(t+=1,`u${e()}${t}`)})();function u(t){let e=[];for(let r=0,n=t.length;r<n;r++)e.push(t[r]);return e}var f=null;function y(t={}){return f||(t.includeStyleProperties?(f=t.includeStyleProperties,f):(f=u(window.getComputedStyle(document.documentElement)),f))}function w(t){return new Promise((e,r)=>{let n=new Image;n.onload=()=>{n.decode().then(()=>{requestAnimationFrame(()=>e(n))})},n.onerror=r,n.crossOrigin="anonymous",n.decoding="async",n.src=t})}async function
|
|
26
|
+
function S(t,e){if(t.match(/^[a-z]+:\/\//i))return t;if(t.match(/^\/\//))return window.location.protocol+t;if(t.match(/^[a-z]+:/i))return t;let r=document.implementation.createHTMLDocument(),n=r.createElement("base"),i=r.createElement("a");return r.head.appendChild(n),r.body.appendChild(i),e&&(n.href=e),i.href=t,i.href}var P=(()=>{let t=0,e=()=>`0000${(Math.random()*36**4<<0).toString(36)}`.slice(-4);return()=>(t+=1,`u${e()}${t}`)})();function u(t){let e=[];for(let r=0,n=t.length;r<n;r++)e.push(t[r]);return e}var f=null;function y(t={}){return f||(t.includeStyleProperties?(f=t.includeStyleProperties,f):(f=u(window.getComputedStyle(document.documentElement)),f))}function w(t){return new Promise((e,r)=>{let n=new Image;n.onload=()=>{n.decode().then(()=>{requestAnimationFrame(()=>e(n))})},n.onerror=r,n.crossOrigin="anonymous",n.decoding="async",n.src=t})}async function I(t){return Promise.resolve().then(()=>new XMLSerializer().serializeToString(t)).then(encodeURIComponent).then(e=>`data:image/svg+xml;charset=utf-8,${e}`)}async function U(t,e,r){let n="http://www.w3.org/2000/svg",i=document.createElementNS(n,"svg"),o=document.createElementNS(n,"foreignObject");return i.setAttribute("width",`${e}`),i.setAttribute("height",`${r}`),i.setAttribute("viewBox",`0 0 ${e} ${r}`),o.setAttribute("width","100%"),o.setAttribute("height","100%"),o.setAttribute("x","0"),o.setAttribute("y","0"),o.setAttribute("externalResourcesRequired","true"),i.appendChild(o),o.appendChild(t),I(i)}var l=(t,e)=>{if(t instanceof e)return!0;let r=Object.getPrototypeOf(t);return r===null?!1:r.constructor.name===e.name||l(r,e)};function V(t){let e=t.getPropertyValue("content");return`${t.cssText} content: '${e.replace(/'|"/g,"")}';`}function M(t,e){return y(e).map(r=>{let n=t.getPropertyValue(r),i=t.getPropertyPriority(r);return`${r}: ${n}${i?" !important":""};`}).join(" ")}function F(t,e,r,n){let i=`.${t}:${e}`,o=r.cssText?V(r):M(r,n);return document.createTextNode(`${i}{${o}}`)}function R(t,e,r,n){let i=window.getComputedStyle(t,r),o=i.getPropertyValue("content");if(o===""||o==="none")return;let a=P();try{e.className=`${e.className} ${a}`}catch{return}let c=document.createElement("style");c.appendChild(F(a,r,i,n)),e.appendChild(c)}function C(t,e,r){R(t,e,":before",r),R(t,e,":after",r)}var T="application/font-woff",$="image/jpeg",H={woff:T,woff2:T,ttf:"application/font-truetype",eot:"application/vnd.ms-fontobject",png:"image/png",jpg:$,jpeg:$,gif:"image/gif",tiff:"image/tiff",svg:"image/svg+xml",webp:"image/webp"};function O(t){let e=/\.([^./]*?)$/g.exec(t);return e?e[1]:""}function m(t){let e=O(t).toLowerCase();return H[e]||""}function k(t){return t.split(/,/)[1]}function p(t){return t.search(/^(data:)/)!==-1}function x(t,e){return`data:${e};base64,${t}`}async function q(t,e,r){let n=await(e?.captureFetch||fetch)(t,e);if(!n.ok)throw new Error(`Resource "${n.url}" not found`);let i=await n.blob();return new Promise((o,a)=>{let c=new FileReader;c.onerror=a,c.onloadend=()=>{try{o(r({res:n,result:c.result}))}catch(s){a(s)}},c.readAsDataURL(i)})}async function h(t,e,r){let n=r.resourceCache||{},i=t;if(n[i]!=null)return n[i];r.cacheBust&&(t+=(/\?/.test(t)?"&":"?")+new Date().getTime());let o;try{let a=await q(t,r.fetchRequestInit,({res:c,result:s})=>(e||(e=c.headers.get("Content-Type")||""),k(s)));o=x(a,e)}catch(a){o=r.imagePlaceholder||"";let c=`Failed to fetch resource: ${t}`;a&&(c=typeof a=="string"?a:a.message),c&&console.warn(c)}return n[i]=o,o}async function _(t){let e=t.toDataURL();return e==="data:,"?t.cloneNode(!1):w(e)}async function v(t,e){if(t.currentSrc){let o=document.createElement("canvas"),a=o.getContext("2d");o.width=t.clientWidth,o.height=t.clientHeight,a?.drawImage(t,0,0,o.width,o.height);let c=o.toDataURL();return w(c)}let r=t.poster,n=m(r),i=await h(r,n,e);return w(i)}async function B(t,e){var r;try{if(!((r=t?.contentDocument)===null||r===void 0)&&r.body)return await b(t.contentDocument.body,e,!0)}catch{}return t.cloneNode(!1)}async function G(t,e){return l(t,HTMLCanvasElement)?_(t).catch(()=>t.cloneNode(!1)):l(t,HTMLVideoElement)?v(t,e).catch(()=>document.createElement("div")):l(t,HTMLIFrameElement)?B(t,e):t.cloneNode(!1)}var j=t=>t.tagName!=null&&t.tagName.toUpperCase()==="SLOT";async function z(t,e,r){var n,i;let o=[];return j(t)&&t.assignedNodes?o=u(t.assignedNodes()):l(t,HTMLIFrameElement)&&(!((n=t.contentDocument)===null||n===void 0)&&n.body)?o=u(t.contentDocument.body.childNodes):o=u(((i=t.shadowRoot)!==null&&i!==void 0?i:t).childNodes),o.length===0||l(t,HTMLVideoElement)||await o.reduce((a,c)=>a.then(()=>b(c,r)).then(s=>{s&&e.appendChild(s)}),Promise.resolve()),e}function W(t,e,r){let n=e.style;if(!n)return;let i=window.getComputedStyle(t);i.cssText?(n.cssText=i.cssText,n.transformOrigin=i.transformOrigin):y(r).forEach(o=>{let a=i.getPropertyValue(o);o==="font-size"&&a.endsWith("px")&&(a=`${parseFloat(a.substring(0,a.length-2))}px`),l(t,HTMLIFrameElement)&&o==="display"&&a==="inline"&&(a="block"),o==="d"&&e.getAttribute("d")&&(a=`path(${e.getAttribute("d")})`),n.setProperty(o,a,i.getPropertyPriority(o))})}function X(t,e){l(t,HTMLTextAreaElement)&&(e.textContent=t.value),l(t,HTMLInputElement)&&e.setAttribute("value",t.value)}function K(t,e){if(l(t,HTMLSelectElement)){let n=Array.from(e.children).find(i=>t.value===i.getAttribute("value"));n&&n.setAttribute("selected","")}}function J(t,e,r){return l(e,Element)&&(W(t,e,r),C(t,e,r),X(t,e),K(t,e)),e}async function Q(t,e){let r=t.querySelectorAll?t.querySelectorAll("use"):[];if(r.length===0)return t;let n={};for(let o=0;o<r.length;o++){let a=r[o],c=a.getAttribute("href")||a.getAttribute("xlink:href");if(c){let s=t.querySelector(c),d=document.querySelector(c);!s&&d&&!n[c]&&(n[c]=await b(d,e,!0))}}let i=Object.values(n);if(i.length){let o="http://www.w3.org/1999/xhtml",a=document.createElementNS(o,"svg");a.setAttribute("xmlns",o),a.style.position="absolute",a.style.width="0",a.style.height="0",a.style.overflow="hidden",a.style.display="none";let c=document.createElementNS(o,"defs");a.appendChild(c);for(let s=0;s<i.length;s++)c.appendChild(i[s]);t.appendChild(a)}return t}async function b(t,e,r){if(await e.beforeClone?.(t),!r&&e.filter&&!e.filter(t))return null;let n=e.placeholder?.(t);return n||Promise.resolve(t).then(i=>G(i,e)).then(i=>z(t,i,e)).then(async i=>(await e.beforeClone?.(t),J(t,i,e))).then(i=>e.afterClone?.(t,i)??i).then(i=>Q(i,e))}var A=/url\((['"]?)([^'"]+?)\1\)/g,L=/url\([^)]+\)\s*format\((["']?)([^"']+)\1\)/g,Y=/src:\s*(?:url\([^)]+\)\s*format\([^)]+\)[,;]\s*)+/g;function Z(t){let e=t.replace(/([.*+?^${}()|\[\]\/\\])/g,"\\$1");return new RegExp(`(url\\(['"]?)(${e})(['"]?\\))`,"g")}function N(t){let e=[];return t.replace(A,(r,n,i)=>(e.push(i),r)),e.filter(r=>!p(r)&&!r.startsWith("#"))}async function tt(t,e,r,n,i){try{let o=r?S(e,r):e,a=m(e),c;if(i){let s=await i(o);c=x(s,a)}else c=await h(o,a,n);return t.replace(Z(e),`$1${c}$3`)}catch{}return t}function et(t,{preferredFontFormat:e}){return e?t.replace(Y,r=>{for(L.lastIndex=0;;){let[n,,i]=L.exec(r)||[];if(!i)return"";if(i===e)return`src: ${n};`}}):t}function rt(t){return t.search(A)!==-1}async function E(t,e,r){if(!rt(t))return t;let n=et(t,r);return N(n).reduce((o,a)=>o.then(c=>tt(c,a,e,r)),Promise.resolve(n))}async function g(t,e,r){var n;let i=(n=e.style)===null||n===void 0?void 0:n.getPropertyValue(t);if(i){let o=await E(i,null,r);return e.style.setProperty(t,o,e.style.getPropertyPriority(t)),!0}return!1}async function nt(t,e){await g("background",t,e)||await g("background-image",t,e),await g("mask",t,e)||await g("-webkit-mask",t,e)||await g("mask-image",t,e)||await g("-webkit-mask-image",t,e)}async function it(t,e){let r=l(t,HTMLImageElement);if(!(r&&!p(t.src))&&!(l(t,SVGImageElement)&&!p(t.href.baseVal)))return;let n=r?t.src:t.href.baseVal,i=await h(n,m(n),e);await new Promise((o,a)=>{t.onload=o,t.onerror=e.onImageErrorHandler?(...s)=>{try{o(e.onImageErrorHandler(...s))}catch(d){a(d)}}:a;let c=t;c.decode&&(c.decode=o),c.loading==="lazy"&&(c.loading="eager"),r?(t.srcset="",t.src=i):t.href.baseVal=i})}async function ot(t,e){let n=u(t.childNodes).map(i=>D(i,e));await Promise.all(n).then(()=>t)}async function D(t,e){await e.beforeClone?.(t),l(t,Element)&&(await nt(t,e),await it(t,e),await ot(t,e))}export{b as cloneNode,D as embedImages,E as embedResources,U as nodeToDataURL};
|
package/src/capture.js
CHANGED
|
@@ -224,7 +224,12 @@ export function createScreenshotCapture({ apiUrl, projectKey, getHeaders = () =>
|
|
|
224
224
|
await untilAborted(captureEngine.embedImages(stage, options));
|
|
225
225
|
checkContext();
|
|
226
226
|
await yieldCapture();
|
|
227
|
-
|
|
227
|
+
// Transparent page backgrounds reveal the browser canvas, not the image viewer.
|
|
228
|
+
const viewport = document.createElement("div");
|
|
229
|
+
viewport.style.backgroundColor = "Canvas";
|
|
230
|
+
viewport.style.colorScheme = rootStyle.colorScheme;
|
|
231
|
+
viewport.append(stage);
|
|
232
|
+
const svg = await captureEngine.nodeToDataURL(viewport, context.width, context.height);
|
|
228
233
|
const image = new Image();
|
|
229
234
|
image.src = svg;
|
|
230
235
|
await untilAborted(image.decode());
|
|
@@ -239,9 +244,6 @@ export function createScreenshotCapture({ apiUrl, projectKey, getHeaders = () =>
|
|
|
239
244
|
let blob = await untilAborted(new Promise(resolve => canvas.toBlob(resolve, "image/png")));
|
|
240
245
|
if (!blob) throw new Error("Screenshot encoding failed");
|
|
241
246
|
if (blob.size > 2800000) {
|
|
242
|
-
ctx.globalCompositeOperation = "destination-over";
|
|
243
|
-
ctx.fillStyle = getComputedStyle(document.documentElement).getPropertyValue("background-color");
|
|
244
|
-
ctx.fillRect(0, 0, context.width, context.height);
|
|
245
247
|
blob = await untilAborted(new Promise(resolve => canvas.toBlob(resolve, "image/jpeg", 0.9)));
|
|
246
248
|
if (!blob) throw new Error("Screenshot encoding failed");
|
|
247
249
|
}
|