├── .gitignore ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── dist ├── html-element-to-image.js ├── html-element-to-image.mjs └── html-element-to-image.umd.js ├── index.html ├── package-lock.json ├── package.json ├── src └── index.ts ├── tsconfig.json ├── types └── index.d.ts └── vite.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.tabSize": 4 3 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Hans Koch 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # html-element-to-image 2 | 📷 Capture an image of any given HTML element. 3 | 4 | ## Install 5 | 6 | ```bash 7 | npm install html-element-to-image 8 | ``` 9 | 10 | ## 📖 Example 11 | 12 | Codepen: https://codepen.io/Hammster/pen/ZEYvxLa 13 | 14 | ESM 15 | ```html 16 | 32 | ``` 33 | 34 | CJS 35 | ``` 36 | const nodeToDataURL = require('html-element-to-image'); 37 | ``` 38 | 39 | UMD 40 | > Exposes the function `nodeToDataURL` to the window/global 41 | 42 | ## 🔬 Differences to Other Libraries 43 | 44 | Compared to [html-to-image](https://github.com/bubkoo/html-to-image), [dom-to-image](https://github.com/tsayen/dom-to-image) and [dom-to-image-more](https://github.com/1904labs/dom-to-image-more) this library uses a different approach, which leads to a big performance increase. Instead of making a [deep copy](https://en.wikipedia.org/wiki/Object_copying#Deep_copy) of the targeted element with applied styles via `el.getComputedStyle()`. This library uses the available DOM for serialization. Before and after the serialization, obfuscated classes get added to hide the parts we don't want to be displayed. 45 | 46 | Another difference is that this library has no issue at all with `SVG Sprite Sheets` and `ShadowDOM` since we use the given DOM already. 47 | 48 | 57 | 58 | ## 🛡️ Limitation 59 | 60 | - If the DOM node you want to render includes a `` element with something drawn on it, it should be handled fine, unless the canvas is [tainted](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image) - in this case rendering will rather not succeed. 61 | - Rendering will failed on huge DOM due to the dataURI [limit variations](https://stackoverflow.com/questions/695151/data-protocol-url-size-limitations/41755526#41755526) in browser implementations. 62 | - Transforms may be affected. Elements that depend on `transform-style: preserve-3d;` do not work in a `foreignObject`, everything get flattened. And this is sadly a requirement for creating a image buffer, and also effects [html-to-image](https://github.com/bubkoo/html-to-image), [dom-to-image](https://github.com/tsayen/dom-to-image) and [dom-to-image-more](https://github.com/1904labs/dom-to-image-more) 63 | - this can be avoided partially by adding a fallback z-index 64 | 65 | ## 💔 Know Issues 66 | 67 | - The canvas is not yet DPI aware 68 | - styles from `html` and `body` can sometimes be overwritten by the [Specificity](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity) 69 | - Lazy loaded components can break rendering 70 | - Shadow DOM 71 | -------------------------------------------------------------------------------- /dist/html-element-to-image.js: -------------------------------------------------------------------------------- 1 | "use strict";const a="___",l=`${a}capture-show`,m=`${a}capture-hide`,u=`${a}force-overflow`,h={customStyle:"",excludedNodes:[],returnType:"dataUrl",targetNode:document.body};let c=h;function p(n){n.classList.add(l);let t=n.parentElement;for(const e of c.excludedNodes)e.classList.add(m);for(;t;)t.classList.add(u),t=t.parentElement}function x(n){n.classList.remove(l);let t=n.parentElement;for(const e of c.excludedNodes)e.classList.remove(m);for(;t;)t.classList.remove(u),t=t.parentElement}function f(n){const t=document.head.cloneNode(!0),e=document.createElement("style");for(const o of Array.from(t.childNodes))o instanceof HTMLStyleElement||o.remove();e.innerText+="foreignObject>div {",e.innerText+="position: fixed;",e.innerText+=`top: ${window.outerHeight}px;`,e.innerText+="overflow: visible;",e.innerText+="}",e.innerText+=`.${l} {`,e.innerText+="position: fixed;",e.innerText+="left: 0;",e.innerText+="top: 0;",e.innerText+=`width: ${n.width}px;`,e.innerText+=`height: ${n.height}px;`,e.innerText+="}",e.innerText+=`.${m} {display: none !important;}`,e.innerText+=`.${u} {overflow: visible !important;}`,e.innerText+=c.customStyle,t.appendChild(e);let i="";for(const o of Array.from(t.querySelectorAll("style")))i+=o.outerHTML;return i}function v(){return document.body.innerHTML.replace(/)<[^<]*)*<\/script>/g,"")}function T(n,t){p(n);const e=f(t),i=v();-t.left,-t.top;const o=document.createElementNS("http://www.w3.org/2000/svg","svg");o.setAttribute("xmlns","http://www.w3.org/2000/svg"),o.setAttribute("height",t.height.toString()),o.setAttribute("width",t.width.toString());const s=document.createElementNS("http://www.w3.org/2000/svg","foreignObject");s.removeAttribute("xmlns"),s.setAttribute("x","0"),s.setAttribute("y","0"),s.setAttribute("height",window.outerHeight.toString()),s.setAttribute("width",window.outerWidth.toString());const r=document.createElement("div");return r.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),r.setAttribute("xmlns:xlink","http://www.w3.org/1999/xlink"),r.setAttribute("class",document.body.className),r.innerHTML=i,s.appendChild(r),o.insertAdjacentHTML("afterbegin",e),o.appendChild(s),x(n),new XMLSerializer().serializeToString(o)}function b(n){c={...h,...n};const t=c.targetNode,e=t.getBoundingClientRect(),i=T(t,e),s=`data:image/svg+xml,${encodeURIComponent(i)}`,r=document.createElement("canvas");return new Promise((g,w)=>{if(!r){w();return}const d=document.createElement("img");d.addEventListener("error",()=>{r.remove(),w()}),d.addEventListener("load",()=>{r.width=e.width,r.height=e.height,r.getContext("2d").drawImage(d,0,0),g(r.toDataURL()),r.remove()}),d.src=s})}module.exports=b; 2 | -------------------------------------------------------------------------------- /dist/html-element-to-image.mjs: -------------------------------------------------------------------------------- 1 | const a = "___", l = `${a}capture-show`, m = `${a}capture-hide`, u = `${a}force-overflow`, h = { 2 | customStyle: "", 3 | excludedNodes: [], 4 | returnType: "dataUrl", 5 | targetNode: document.body 6 | }; 7 | let c = h; 8 | function p(n) { 9 | n.classList.add(l); 10 | let t = n.parentElement; 11 | for (const e of c.excludedNodes) 12 | e.classList.add(m); 13 | for (; t; ) 14 | t.classList.add(u), t = t.parentElement; 15 | } 16 | function x(n) { 17 | n.classList.remove(l); 18 | let t = n.parentElement; 19 | for (const e of c.excludedNodes) 20 | e.classList.remove(m); 21 | for (; t; ) 22 | t.classList.remove(u), t = t.parentElement; 23 | } 24 | function f(n) { 25 | const t = document.head.cloneNode(!0), e = document.createElement("style"); 26 | for (const r of Array.from(t.childNodes)) 27 | r instanceof HTMLStyleElement || r.remove(); 28 | e.innerText += "foreignObject>div {", e.innerText += "position: fixed;", e.innerText += `top: ${window.outerHeight}px;`, e.innerText += "overflow: visible;", e.innerText += "}", e.innerText += `.${l} {`, e.innerText += "position: fixed;", e.innerText += "left: 0;", e.innerText += "top: 0;", e.innerText += `width: ${n.width}px;`, e.innerText += `height: ${n.height}px;`, e.innerText += "}", e.innerText += `.${m} {display: none !important;}`, e.innerText += `.${u} {overflow: visible !important;}`, e.innerText += c.customStyle, t.appendChild(e); 29 | let i = ""; 30 | for (const r of Array.from(t.querySelectorAll("style"))) 31 | i += r.outerHTML; 32 | return i; 33 | } 34 | function v() { 35 | return document.body.innerHTML.replace(/)<[^<]*)*<\/script>/g, ""); 36 | } 37 | function T(n, t) { 38 | p(n); 39 | const e = f(t), i = v(); 40 | -t.left, -t.top; 41 | const r = document.createElementNS("http://www.w3.org/2000/svg", "svg"); 42 | r.setAttribute("xmlns", "http://www.w3.org/2000/svg"), r.setAttribute("height", t.height.toString()), r.setAttribute("width", t.width.toString()); 43 | const s = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject"); 44 | s.removeAttribute("xmlns"), s.setAttribute("x", "0"), s.setAttribute("y", "0"), s.setAttribute("height", window.outerHeight.toString()), s.setAttribute("width", window.outerWidth.toString()); 45 | const o = document.createElement("div"); 46 | return o.setAttribute("xmlns", "http://www.w3.org/1999/xhtml"), o.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink"), o.setAttribute("class", document.body.className), o.innerHTML = i, s.appendChild(o), r.insertAdjacentHTML("afterbegin", e), r.appendChild(s), x(n), new XMLSerializer().serializeToString(r); 47 | } 48 | function y(n) { 49 | c = { ...h, ...n }; 50 | const t = c.targetNode, e = t.getBoundingClientRect(), i = T(t, e), s = `data:image/svg+xml,${encodeURIComponent(i)}`, o = document.createElement("canvas"); 51 | return new Promise((g, w) => { 52 | if (!o) { 53 | w(); 54 | return; 55 | } 56 | const d = document.createElement("img"); 57 | d.addEventListener("error", () => { 58 | o.remove(), w(); 59 | }), d.addEventListener("load", () => { 60 | o.width = e.width, o.height = e.height, o.getContext("2d").drawImage(d, 0, 0), g(o.toDataURL()), o.remove(); 61 | }), d.src = s; 62 | }); 63 | } 64 | export { 65 | y as default 66 | }; 67 | -------------------------------------------------------------------------------- /dist/html-element-to-image.umd.js: -------------------------------------------------------------------------------- 1 | (function(s,d){typeof exports=="object"&&typeof module<"u"?module.exports=d():typeof define=="function"&&define.amd?define(d):(s=typeof globalThis<"u"?globalThis:s||self,s.nodeToDataURL=d())})(this,function(){"use strict";const s="___",d=`${s}capture-show`,u=`${s}capture-hide`,m=`${s}force-overflow`,f={customStyle:"",excludedNodes:[],returnType:"dataUrl",targetNode:document.body};let a=f;function g(n){n.classList.add(d);let t=n.parentElement;for(const e of a.excludedNodes)e.classList.add(u);for(;t;)t.classList.add(m),t=t.parentElement}function w(n){n.classList.remove(d);let t=n.parentElement;for(const e of a.excludedNodes)e.classList.remove(u);for(;t;)t.classList.remove(m),t=t.parentElement}function x(n){const t=document.head.cloneNode(!0),e=document.createElement("style");for(const r of Array.from(t.childNodes))r instanceof HTMLStyleElement||r.remove();e.innerText+="foreignObject>div {",e.innerText+="position: fixed;",e.innerText+=`top: ${window.outerHeight}px;`,e.innerText+="overflow: visible;",e.innerText+="}",e.innerText+=`.${d} {`,e.innerText+="position: fixed;",e.innerText+="left: 0;",e.innerText+="top: 0;",e.innerText+=`width: ${n.width}px;`,e.innerText+=`height: ${n.height}px;`,e.innerText+="}",e.innerText+=`.${u} {display: none !important;}`,e.innerText+=`.${m} {overflow: visible !important;}`,e.innerText+=a.customStyle,t.appendChild(e);let c="";for(const r of Array.from(t.querySelectorAll("style")))c+=r.outerHTML;return c}function v(){return document.body.innerHTML.replace(/)<[^<]*)*<\/script>/g,"")}function T(n,t){g(n);const e=x(t),c=v();-t.left,-t.top;const r=document.createElementNS("http://www.w3.org/2000/svg","svg");r.setAttribute("xmlns","http://www.w3.org/2000/svg"),r.setAttribute("height",t.height.toString()),r.setAttribute("width",t.width.toString());const i=document.createElementNS("http://www.w3.org/2000/svg","foreignObject");i.removeAttribute("xmlns"),i.setAttribute("x","0"),i.setAttribute("y","0"),i.setAttribute("height",window.outerHeight.toString()),i.setAttribute("width",window.outerWidth.toString());const o=document.createElement("div");return o.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),o.setAttribute("xmlns:xlink","http://www.w3.org/1999/xlink"),o.setAttribute("class",document.body.className),o.innerHTML=c,i.appendChild(o),r.insertAdjacentHTML("afterbegin",e),r.appendChild(i),w(n),new XMLSerializer().serializeToString(r)}function b(n){a={...f,...n};const t=a.targetNode,e=t.getBoundingClientRect(),c=T(t,e),i=`data:image/svg+xml,${encodeURIComponent(c)}`,o=document.createElement("canvas");return new Promise((h,p)=>{if(!o){p();return}const l=document.createElement("img");l.addEventListener("error",()=>{o.remove(),p()}),l.addEventListener("load",()=>{o.width=e.width,o.height=e.height,o.getContext("2d").drawImage(l,0,0),h(o.toDataURL()),o.remove()}),l.src=i})}return b}); 2 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 35 | 36 | 37 | Source: 38 |
39 |
40 | T説🧜pѬѬasѪ"§§)("!编/)$=?!°&%)?§"$(§sw汉字编码§"$(§sw汉字方法orФdpѬѬasѪ"§§)("!/)$=?!°&%)?编码方法orФd 41 |
42 |
43 |
44 | 45 | Target (Copy without child and overloaded style): 46 |
47 | 48 |
49 | 65 | 66 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "html-element-to-image", 3 | "version": "2.2.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "html-element-to-image", 9 | "version": "2.2.0", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "typescript": "^5.4.5", 13 | "vite": "^5.2.12" 14 | } 15 | }, 16 | "node_modules/@esbuild/aix-ppc64": { 17 | "version": "0.20.2", 18 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", 19 | "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", 20 | "cpu": [ 21 | "ppc64" 22 | ], 23 | "dev": true, 24 | "optional": true, 25 | "os": [ 26 | "aix" 27 | ], 28 | "engines": { 29 | "node": ">=12" 30 | } 31 | }, 32 | "node_modules/@esbuild/android-arm": { 33 | "version": "0.20.2", 34 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", 35 | "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", 36 | "cpu": [ 37 | "arm" 38 | ], 39 | "dev": true, 40 | "optional": true, 41 | "os": [ 42 | "android" 43 | ], 44 | "engines": { 45 | "node": ">=12" 46 | } 47 | }, 48 | "node_modules/@esbuild/android-arm64": { 49 | "version": "0.20.2", 50 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", 51 | "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", 52 | "cpu": [ 53 | "arm64" 54 | ], 55 | "dev": true, 56 | "optional": true, 57 | "os": [ 58 | "android" 59 | ], 60 | "engines": { 61 | "node": ">=12" 62 | } 63 | }, 64 | "node_modules/@esbuild/android-x64": { 65 | "version": "0.20.2", 66 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", 67 | "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", 68 | "cpu": [ 69 | "x64" 70 | ], 71 | "dev": true, 72 | "optional": true, 73 | "os": [ 74 | "android" 75 | ], 76 | "engines": { 77 | "node": ">=12" 78 | } 79 | }, 80 | "node_modules/@esbuild/darwin-arm64": { 81 | "version": "0.20.2", 82 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", 83 | "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", 84 | "cpu": [ 85 | "arm64" 86 | ], 87 | "dev": true, 88 | "optional": true, 89 | "os": [ 90 | "darwin" 91 | ], 92 | "engines": { 93 | "node": ">=12" 94 | } 95 | }, 96 | "node_modules/@esbuild/darwin-x64": { 97 | "version": "0.20.2", 98 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", 99 | "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", 100 | "cpu": [ 101 | "x64" 102 | ], 103 | "dev": true, 104 | "optional": true, 105 | "os": [ 106 | "darwin" 107 | ], 108 | "engines": { 109 | "node": ">=12" 110 | } 111 | }, 112 | "node_modules/@esbuild/freebsd-arm64": { 113 | "version": "0.20.2", 114 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", 115 | "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", 116 | "cpu": [ 117 | "arm64" 118 | ], 119 | "dev": true, 120 | "optional": true, 121 | "os": [ 122 | "freebsd" 123 | ], 124 | "engines": { 125 | "node": ">=12" 126 | } 127 | }, 128 | "node_modules/@esbuild/freebsd-x64": { 129 | "version": "0.20.2", 130 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", 131 | "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", 132 | "cpu": [ 133 | "x64" 134 | ], 135 | "dev": true, 136 | "optional": true, 137 | "os": [ 138 | "freebsd" 139 | ], 140 | "engines": { 141 | "node": ">=12" 142 | } 143 | }, 144 | "node_modules/@esbuild/linux-arm": { 145 | "version": "0.20.2", 146 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", 147 | "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", 148 | "cpu": [ 149 | "arm" 150 | ], 151 | "dev": true, 152 | "optional": true, 153 | "os": [ 154 | "linux" 155 | ], 156 | "engines": { 157 | "node": ">=12" 158 | } 159 | }, 160 | "node_modules/@esbuild/linux-arm64": { 161 | "version": "0.20.2", 162 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", 163 | "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", 164 | "cpu": [ 165 | "arm64" 166 | ], 167 | "dev": true, 168 | "optional": true, 169 | "os": [ 170 | "linux" 171 | ], 172 | "engines": { 173 | "node": ">=12" 174 | } 175 | }, 176 | "node_modules/@esbuild/linux-ia32": { 177 | "version": "0.20.2", 178 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", 179 | "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", 180 | "cpu": [ 181 | "ia32" 182 | ], 183 | "dev": true, 184 | "optional": true, 185 | "os": [ 186 | "linux" 187 | ], 188 | "engines": { 189 | "node": ">=12" 190 | } 191 | }, 192 | "node_modules/@esbuild/linux-loong64": { 193 | "version": "0.20.2", 194 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", 195 | "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", 196 | "cpu": [ 197 | "loong64" 198 | ], 199 | "dev": true, 200 | "optional": true, 201 | "os": [ 202 | "linux" 203 | ], 204 | "engines": { 205 | "node": ">=12" 206 | } 207 | }, 208 | "node_modules/@esbuild/linux-mips64el": { 209 | "version": "0.20.2", 210 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", 211 | "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", 212 | "cpu": [ 213 | "mips64el" 214 | ], 215 | "dev": true, 216 | "optional": true, 217 | "os": [ 218 | "linux" 219 | ], 220 | "engines": { 221 | "node": ">=12" 222 | } 223 | }, 224 | "node_modules/@esbuild/linux-ppc64": { 225 | "version": "0.20.2", 226 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", 227 | "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", 228 | "cpu": [ 229 | "ppc64" 230 | ], 231 | "dev": true, 232 | "optional": true, 233 | "os": [ 234 | "linux" 235 | ], 236 | "engines": { 237 | "node": ">=12" 238 | } 239 | }, 240 | "node_modules/@esbuild/linux-riscv64": { 241 | "version": "0.20.2", 242 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", 243 | "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", 244 | "cpu": [ 245 | "riscv64" 246 | ], 247 | "dev": true, 248 | "optional": true, 249 | "os": [ 250 | "linux" 251 | ], 252 | "engines": { 253 | "node": ">=12" 254 | } 255 | }, 256 | "node_modules/@esbuild/linux-s390x": { 257 | "version": "0.20.2", 258 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", 259 | "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", 260 | "cpu": [ 261 | "s390x" 262 | ], 263 | "dev": true, 264 | "optional": true, 265 | "os": [ 266 | "linux" 267 | ], 268 | "engines": { 269 | "node": ">=12" 270 | } 271 | }, 272 | "node_modules/@esbuild/linux-x64": { 273 | "version": "0.20.2", 274 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", 275 | "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", 276 | "cpu": [ 277 | "x64" 278 | ], 279 | "dev": true, 280 | "optional": true, 281 | "os": [ 282 | "linux" 283 | ], 284 | "engines": { 285 | "node": ">=12" 286 | } 287 | }, 288 | "node_modules/@esbuild/netbsd-x64": { 289 | "version": "0.20.2", 290 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", 291 | "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", 292 | "cpu": [ 293 | "x64" 294 | ], 295 | "dev": true, 296 | "optional": true, 297 | "os": [ 298 | "netbsd" 299 | ], 300 | "engines": { 301 | "node": ">=12" 302 | } 303 | }, 304 | "node_modules/@esbuild/openbsd-x64": { 305 | "version": "0.20.2", 306 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", 307 | "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", 308 | "cpu": [ 309 | "x64" 310 | ], 311 | "dev": true, 312 | "optional": true, 313 | "os": [ 314 | "openbsd" 315 | ], 316 | "engines": { 317 | "node": ">=12" 318 | } 319 | }, 320 | "node_modules/@esbuild/sunos-x64": { 321 | "version": "0.20.2", 322 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", 323 | "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", 324 | "cpu": [ 325 | "x64" 326 | ], 327 | "dev": true, 328 | "optional": true, 329 | "os": [ 330 | "sunos" 331 | ], 332 | "engines": { 333 | "node": ">=12" 334 | } 335 | }, 336 | "node_modules/@esbuild/win32-arm64": { 337 | "version": "0.20.2", 338 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", 339 | "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", 340 | "cpu": [ 341 | "arm64" 342 | ], 343 | "dev": true, 344 | "optional": true, 345 | "os": [ 346 | "win32" 347 | ], 348 | "engines": { 349 | "node": ">=12" 350 | } 351 | }, 352 | "node_modules/@esbuild/win32-ia32": { 353 | "version": "0.20.2", 354 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", 355 | "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", 356 | "cpu": [ 357 | "ia32" 358 | ], 359 | "dev": true, 360 | "optional": true, 361 | "os": [ 362 | "win32" 363 | ], 364 | "engines": { 365 | "node": ">=12" 366 | } 367 | }, 368 | "node_modules/@esbuild/win32-x64": { 369 | "version": "0.20.2", 370 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", 371 | "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", 372 | "cpu": [ 373 | "x64" 374 | ], 375 | "dev": true, 376 | "optional": true, 377 | "os": [ 378 | "win32" 379 | ], 380 | "engines": { 381 | "node": ">=12" 382 | } 383 | }, 384 | "node_modules/@jridgewell/gen-mapping": { 385 | "version": "0.3.3", 386 | "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", 387 | "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", 388 | "dev": true, 389 | "optional": true, 390 | "peer": true, 391 | "dependencies": { 392 | "@jridgewell/set-array": "^1.0.1", 393 | "@jridgewell/sourcemap-codec": "^1.4.10", 394 | "@jridgewell/trace-mapping": "^0.3.9" 395 | }, 396 | "engines": { 397 | "node": ">=6.0.0" 398 | } 399 | }, 400 | "node_modules/@jridgewell/resolve-uri": { 401 | "version": "3.1.0", 402 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", 403 | "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", 404 | "dev": true, 405 | "optional": true, 406 | "peer": true, 407 | "engines": { 408 | "node": ">=6.0.0" 409 | } 410 | }, 411 | "node_modules/@jridgewell/set-array": { 412 | "version": "1.1.2", 413 | "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", 414 | "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", 415 | "dev": true, 416 | "optional": true, 417 | "peer": true, 418 | "engines": { 419 | "node": ">=6.0.0" 420 | } 421 | }, 422 | "node_modules/@jridgewell/source-map": { 423 | "version": "0.3.5", 424 | "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", 425 | "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", 426 | "dev": true, 427 | "optional": true, 428 | "peer": true, 429 | "dependencies": { 430 | "@jridgewell/gen-mapping": "^0.3.0", 431 | "@jridgewell/trace-mapping": "^0.3.9" 432 | } 433 | }, 434 | "node_modules/@jridgewell/sourcemap-codec": { 435 | "version": "1.4.15", 436 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", 437 | "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", 438 | "dev": true, 439 | "optional": true, 440 | "peer": true 441 | }, 442 | "node_modules/@jridgewell/trace-mapping": { 443 | "version": "0.3.18", 444 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", 445 | "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", 446 | "dev": true, 447 | "optional": true, 448 | "peer": true, 449 | "dependencies": { 450 | "@jridgewell/resolve-uri": "3.1.0", 451 | "@jridgewell/sourcemap-codec": "1.4.14" 452 | } 453 | }, 454 | "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { 455 | "version": "1.4.14", 456 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", 457 | "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", 458 | "dev": true, 459 | "optional": true, 460 | "peer": true 461 | }, 462 | "node_modules/@rollup/rollup-android-arm-eabi": { 463 | "version": "4.18.0", 464 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", 465 | "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", 466 | "cpu": [ 467 | "arm" 468 | ], 469 | "dev": true, 470 | "optional": true, 471 | "os": [ 472 | "android" 473 | ] 474 | }, 475 | "node_modules/@rollup/rollup-android-arm64": { 476 | "version": "4.18.0", 477 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", 478 | "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", 479 | "cpu": [ 480 | "arm64" 481 | ], 482 | "dev": true, 483 | "optional": true, 484 | "os": [ 485 | "android" 486 | ] 487 | }, 488 | "node_modules/@rollup/rollup-darwin-arm64": { 489 | "version": "4.18.0", 490 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", 491 | "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", 492 | "cpu": [ 493 | "arm64" 494 | ], 495 | "dev": true, 496 | "optional": true, 497 | "os": [ 498 | "darwin" 499 | ] 500 | }, 501 | "node_modules/@rollup/rollup-darwin-x64": { 502 | "version": "4.18.0", 503 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", 504 | "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", 505 | "cpu": [ 506 | "x64" 507 | ], 508 | "dev": true, 509 | "optional": true, 510 | "os": [ 511 | "darwin" 512 | ] 513 | }, 514 | "node_modules/@rollup/rollup-linux-arm-gnueabihf": { 515 | "version": "4.18.0", 516 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", 517 | "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", 518 | "cpu": [ 519 | "arm" 520 | ], 521 | "dev": true, 522 | "optional": true, 523 | "os": [ 524 | "linux" 525 | ] 526 | }, 527 | "node_modules/@rollup/rollup-linux-arm-musleabihf": { 528 | "version": "4.18.0", 529 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", 530 | "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", 531 | "cpu": [ 532 | "arm" 533 | ], 534 | "dev": true, 535 | "optional": true, 536 | "os": [ 537 | "linux" 538 | ] 539 | }, 540 | "node_modules/@rollup/rollup-linux-arm64-gnu": { 541 | "version": "4.18.0", 542 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", 543 | "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", 544 | "cpu": [ 545 | "arm64" 546 | ], 547 | "dev": true, 548 | "optional": true, 549 | "os": [ 550 | "linux" 551 | ] 552 | }, 553 | "node_modules/@rollup/rollup-linux-arm64-musl": { 554 | "version": "4.18.0", 555 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", 556 | "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", 557 | "cpu": [ 558 | "arm64" 559 | ], 560 | "dev": true, 561 | "optional": true, 562 | "os": [ 563 | "linux" 564 | ] 565 | }, 566 | "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { 567 | "version": "4.18.0", 568 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", 569 | "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", 570 | "cpu": [ 571 | "ppc64" 572 | ], 573 | "dev": true, 574 | "optional": true, 575 | "os": [ 576 | "linux" 577 | ] 578 | }, 579 | "node_modules/@rollup/rollup-linux-riscv64-gnu": { 580 | "version": "4.18.0", 581 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", 582 | "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", 583 | "cpu": [ 584 | "riscv64" 585 | ], 586 | "dev": true, 587 | "optional": true, 588 | "os": [ 589 | "linux" 590 | ] 591 | }, 592 | "node_modules/@rollup/rollup-linux-s390x-gnu": { 593 | "version": "4.18.0", 594 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", 595 | "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", 596 | "cpu": [ 597 | "s390x" 598 | ], 599 | "dev": true, 600 | "optional": true, 601 | "os": [ 602 | "linux" 603 | ] 604 | }, 605 | "node_modules/@rollup/rollup-linux-x64-gnu": { 606 | "version": "4.18.0", 607 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", 608 | "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", 609 | "cpu": [ 610 | "x64" 611 | ], 612 | "dev": true, 613 | "optional": true, 614 | "os": [ 615 | "linux" 616 | ] 617 | }, 618 | "node_modules/@rollup/rollup-linux-x64-musl": { 619 | "version": "4.18.0", 620 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", 621 | "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", 622 | "cpu": [ 623 | "x64" 624 | ], 625 | "dev": true, 626 | "optional": true, 627 | "os": [ 628 | "linux" 629 | ] 630 | }, 631 | "node_modules/@rollup/rollup-win32-arm64-msvc": { 632 | "version": "4.18.0", 633 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", 634 | "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", 635 | "cpu": [ 636 | "arm64" 637 | ], 638 | "dev": true, 639 | "optional": true, 640 | "os": [ 641 | "win32" 642 | ] 643 | }, 644 | "node_modules/@rollup/rollup-win32-ia32-msvc": { 645 | "version": "4.18.0", 646 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", 647 | "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", 648 | "cpu": [ 649 | "ia32" 650 | ], 651 | "dev": true, 652 | "optional": true, 653 | "os": [ 654 | "win32" 655 | ] 656 | }, 657 | "node_modules/@rollup/rollup-win32-x64-msvc": { 658 | "version": "4.18.0", 659 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", 660 | "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", 661 | "cpu": [ 662 | "x64" 663 | ], 664 | "dev": true, 665 | "optional": true, 666 | "os": [ 667 | "win32" 668 | ] 669 | }, 670 | "node_modules/@types/estree": { 671 | "version": "1.0.5", 672 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", 673 | "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", 674 | "dev": true 675 | }, 676 | "node_modules/acorn": { 677 | "version": "8.10.0", 678 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", 679 | "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", 680 | "dev": true, 681 | "optional": true, 682 | "peer": true, 683 | "bin": { 684 | "acorn": "bin/acorn" 685 | }, 686 | "engines": { 687 | "node": ">=0.4.0" 688 | } 689 | }, 690 | "node_modules/buffer-from": { 691 | "version": "1.1.2", 692 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 693 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", 694 | "dev": true, 695 | "optional": true, 696 | "peer": true 697 | }, 698 | "node_modules/commander": { 699 | "version": "2.20.3", 700 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 701 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 702 | "dev": true, 703 | "optional": true, 704 | "peer": true 705 | }, 706 | "node_modules/esbuild": { 707 | "version": "0.20.2", 708 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", 709 | "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", 710 | "dev": true, 711 | "hasInstallScript": true, 712 | "bin": { 713 | "esbuild": "bin/esbuild" 714 | }, 715 | "engines": { 716 | "node": ">=12" 717 | }, 718 | "optionalDependencies": { 719 | "@esbuild/aix-ppc64": "0.20.2", 720 | "@esbuild/android-arm": "0.20.2", 721 | "@esbuild/android-arm64": "0.20.2", 722 | "@esbuild/android-x64": "0.20.2", 723 | "@esbuild/darwin-arm64": "0.20.2", 724 | "@esbuild/darwin-x64": "0.20.2", 725 | "@esbuild/freebsd-arm64": "0.20.2", 726 | "@esbuild/freebsd-x64": "0.20.2", 727 | "@esbuild/linux-arm": "0.20.2", 728 | "@esbuild/linux-arm64": "0.20.2", 729 | "@esbuild/linux-ia32": "0.20.2", 730 | "@esbuild/linux-loong64": "0.20.2", 731 | "@esbuild/linux-mips64el": "0.20.2", 732 | "@esbuild/linux-ppc64": "0.20.2", 733 | "@esbuild/linux-riscv64": "0.20.2", 734 | "@esbuild/linux-s390x": "0.20.2", 735 | "@esbuild/linux-x64": "0.20.2", 736 | "@esbuild/netbsd-x64": "0.20.2", 737 | "@esbuild/openbsd-x64": "0.20.2", 738 | "@esbuild/sunos-x64": "0.20.2", 739 | "@esbuild/win32-arm64": "0.20.2", 740 | "@esbuild/win32-ia32": "0.20.2", 741 | "@esbuild/win32-x64": "0.20.2" 742 | } 743 | }, 744 | "node_modules/fsevents": { 745 | "version": "2.3.3", 746 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 747 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 748 | "dev": true, 749 | "hasInstallScript": true, 750 | "optional": true, 751 | "os": [ 752 | "darwin" 753 | ], 754 | "engines": { 755 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 756 | } 757 | }, 758 | "node_modules/nanoid": { 759 | "version": "3.3.7", 760 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", 761 | "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", 762 | "dev": true, 763 | "funding": [ 764 | { 765 | "type": "github", 766 | "url": "https://github.com/sponsors/ai" 767 | } 768 | ], 769 | "bin": { 770 | "nanoid": "bin/nanoid.cjs" 771 | }, 772 | "engines": { 773 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 774 | } 775 | }, 776 | "node_modules/picocolors": { 777 | "version": "1.0.1", 778 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", 779 | "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", 780 | "dev": true 781 | }, 782 | "node_modules/postcss": { 783 | "version": "8.4.38", 784 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", 785 | "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", 786 | "dev": true, 787 | "funding": [ 788 | { 789 | "type": "opencollective", 790 | "url": "https://opencollective.com/postcss/" 791 | }, 792 | { 793 | "type": "tidelift", 794 | "url": "https://tidelift.com/funding/github/npm/postcss" 795 | }, 796 | { 797 | "type": "github", 798 | "url": "https://github.com/sponsors/ai" 799 | } 800 | ], 801 | "dependencies": { 802 | "nanoid": "^3.3.7", 803 | "picocolors": "^1.0.0", 804 | "source-map-js": "^1.2.0" 805 | }, 806 | "engines": { 807 | "node": "^10 || ^12 || >=14" 808 | } 809 | }, 810 | "node_modules/source-map": { 811 | "version": "0.6.1", 812 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 813 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 814 | "dev": true, 815 | "optional": true, 816 | "peer": true, 817 | "engines": { 818 | "node": ">=0.10.0" 819 | } 820 | }, 821 | "node_modules/source-map-js": { 822 | "version": "1.2.0", 823 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", 824 | "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", 825 | "dev": true, 826 | "engines": { 827 | "node": ">=0.10.0" 828 | } 829 | }, 830 | "node_modules/source-map-support": { 831 | "version": "0.5.21", 832 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", 833 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", 834 | "dev": true, 835 | "optional": true, 836 | "peer": true, 837 | "dependencies": { 838 | "buffer-from": "^1.0.0", 839 | "source-map": "^0.6.0" 840 | } 841 | }, 842 | "node_modules/terser": { 843 | "version": "5.19.2", 844 | "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.2.tgz", 845 | "integrity": "sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==", 846 | "dev": true, 847 | "optional": true, 848 | "peer": true, 849 | "dependencies": { 850 | "@jridgewell/source-map": "^0.3.3", 851 | "acorn": "^8.8.2", 852 | "commander": "^2.20.0", 853 | "source-map-support": "~0.5.20" 854 | }, 855 | "bin": { 856 | "terser": "bin/terser" 857 | }, 858 | "engines": { 859 | "node": ">=10" 860 | } 861 | }, 862 | "node_modules/typescript": { 863 | "version": "5.4.5", 864 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", 865 | "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", 866 | "dev": true, 867 | "bin": { 868 | "tsc": "bin/tsc", 869 | "tsserver": "bin/tsserver" 870 | }, 871 | "engines": { 872 | "node": ">=14.17" 873 | } 874 | }, 875 | "node_modules/vite": { 876 | "version": "5.2.12", 877 | "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.12.tgz", 878 | "integrity": "sha512-/gC8GxzxMK5ntBwb48pR32GGhENnjtY30G4A0jemunsBkiEZFw60s8InGpN8gkhHEkjnRK1aSAxeQgwvFhUHAA==", 879 | "dev": true, 880 | "dependencies": { 881 | "esbuild": "^0.20.1", 882 | "postcss": "^8.4.38", 883 | "rollup": "^4.13.0" 884 | }, 885 | "bin": { 886 | "vite": "bin/vite.js" 887 | }, 888 | "engines": { 889 | "node": "^18.0.0 || >=20.0.0" 890 | }, 891 | "funding": { 892 | "url": "https://github.com/vitejs/vite?sponsor=1" 893 | }, 894 | "optionalDependencies": { 895 | "fsevents": "~2.3.3" 896 | }, 897 | "peerDependencies": { 898 | "@types/node": "^18.0.0 || >=20.0.0", 899 | "less": "*", 900 | "lightningcss": "^1.21.0", 901 | "sass": "*", 902 | "stylus": "*", 903 | "sugarss": "*", 904 | "terser": "^5.4.0" 905 | }, 906 | "peerDependenciesMeta": { 907 | "@types/node": { 908 | "optional": true 909 | }, 910 | "less": { 911 | "optional": true 912 | }, 913 | "lightningcss": { 914 | "optional": true 915 | }, 916 | "sass": { 917 | "optional": true 918 | }, 919 | "stylus": { 920 | "optional": true 921 | }, 922 | "sugarss": { 923 | "optional": true 924 | }, 925 | "terser": { 926 | "optional": true 927 | } 928 | } 929 | }, 930 | "node_modules/vite/node_modules/rollup": { 931 | "version": "4.18.0", 932 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", 933 | "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", 934 | "dev": true, 935 | "dependencies": { 936 | "@types/estree": "1.0.5" 937 | }, 938 | "bin": { 939 | "rollup": "dist/bin/rollup" 940 | }, 941 | "engines": { 942 | "node": ">=18.0.0", 943 | "npm": ">=8.0.0" 944 | }, 945 | "optionalDependencies": { 946 | "@rollup/rollup-android-arm-eabi": "4.18.0", 947 | "@rollup/rollup-android-arm64": "4.18.0", 948 | "@rollup/rollup-darwin-arm64": "4.18.0", 949 | "@rollup/rollup-darwin-x64": "4.18.0", 950 | "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", 951 | "@rollup/rollup-linux-arm-musleabihf": "4.18.0", 952 | "@rollup/rollup-linux-arm64-gnu": "4.18.0", 953 | "@rollup/rollup-linux-arm64-musl": "4.18.0", 954 | "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", 955 | "@rollup/rollup-linux-riscv64-gnu": "4.18.0", 956 | "@rollup/rollup-linux-s390x-gnu": "4.18.0", 957 | "@rollup/rollup-linux-x64-gnu": "4.18.0", 958 | "@rollup/rollup-linux-x64-musl": "4.18.0", 959 | "@rollup/rollup-win32-arm64-msvc": "4.18.0", 960 | "@rollup/rollup-win32-ia32-msvc": "4.18.0", 961 | "@rollup/rollup-win32-x64-msvc": "4.18.0", 962 | "fsevents": "~2.3.2" 963 | } 964 | } 965 | } 966 | } 967 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "html-element-to-image", 3 | "version": "3.0.0", 4 | "description": "Capture a image from any given HTML element, fast.", 5 | "repository": "https://github.com/Hammster/html-element-to-image", 6 | "author": "Hans Koch ", 7 | "license": "MIT", 8 | "types": "./types/index.d.ts", 9 | "scripts": { 10 | "build": "vite build", 11 | "dev": "vite" 12 | }, 13 | "devDependencies": { 14 | "vite": "^5.2.12", 15 | "typescript": "^5.4.5" 16 | }, 17 | "main": "./dist/html-element-to-image.js", 18 | "module": "./dist/html-element-to-image.mjs", 19 | "exports": { 20 | ".": { 21 | "import": "./dist/html-element-to-image.mjs", 22 | "require": "./dist/html-element-to-image.js" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import type { CaptureOptions } from '../types/index' 2 | 3 | const prefix = '___' 4 | const captureShowClass = `${prefix}capture-show` 5 | const captureHideClass = `${prefix}capture-hide` 6 | const forceOverflowClass = `${prefix}force-overflow` 7 | 8 | const defaultOptions: CaptureOptions = { 9 | customStyle: '', 10 | excludedNodes: [], 11 | returnType: 'dataUrl', 12 | targetNode: document.body 13 | } 14 | 15 | let config: CaptureOptions = defaultOptions 16 | 17 | function addClasses (node: Element): void { 18 | node.classList.add(captureShowClass) 19 | let nodeParent = node.parentElement 20 | 21 | for (const excludedEl of config.excludedNodes) { 22 | excludedEl.classList.add(captureHideClass) 23 | } 24 | 25 | while (nodeParent) { 26 | nodeParent.classList.add(forceOverflowClass) 27 | nodeParent = nodeParent.parentElement 28 | } 29 | } 30 | 31 | function removeClasses (node: Element): void { 32 | node.classList.remove(captureShowClass) 33 | let nodeParent = node.parentElement 34 | 35 | for (const excludedEl of config.excludedNodes) { 36 | excludedEl.classList.remove(captureHideClass) 37 | } 38 | 39 | while (nodeParent) { 40 | nodeParent.classList.remove(forceOverflowClass) 41 | nodeParent = nodeParent.parentElement 42 | } 43 | } 44 | 45 | function serializeHead (elBoundingClientRect: DOMRect): string { 46 | const headClone = document.head.cloneNode(true) as HTMLHeadElement 47 | const captureStyle = document.createElement('style') 48 | 49 | // Only the styled from the head are releavant so we grab them 50 | for (const childElement of Array.from(headClone.childNodes)) { 51 | if (!(childElement instanceof HTMLStyleElement)) { 52 | childElement.remove() 53 | } 54 | } 55 | 56 | // These are the style we apply on the element to move the body out off the visible area. 57 | // The remaining element will be moved back to its original position so it can be rendere without any background. 58 | // Addition Hint: Lineabreaks are parsed as
by the XMLParser so i need to have that syle in one line 59 | captureStyle.innerText += `foreignObject>div {` 60 | captureStyle.innerText += `position: fixed;` 61 | captureStyle.innerText += `top: ${window.outerHeight}px;` 62 | captureStyle.innerText += `overflow: visible;` 63 | captureStyle.innerText += `}` 64 | 65 | captureStyle.innerText += `.${captureShowClass} {` 66 | captureStyle.innerText += `position: fixed;` 67 | captureStyle.innerText += `left: 0;` 68 | captureStyle.innerText += `top: 0;` 69 | captureStyle.innerText += `width: ${elBoundingClientRect.width}px;` 70 | captureStyle.innerText += `height: ${elBoundingClientRect.height}px;` 71 | captureStyle.innerText += `}` 72 | 73 | captureStyle.innerText += `.${captureHideClass} {display: none !important;}` 74 | captureStyle.innerText += `.${forceOverflowClass} {overflow: visible !important;}` 75 | 76 | captureStyle.innerText += config.customStyle 77 | headClone.appendChild(captureStyle) 78 | 79 | let resultHtml = '' 80 | 81 | for (const styleElement of Array.from(headClone.querySelectorAll('style'))) { 82 | resultHtml += styleElement.outerHTML 83 | } 84 | 85 | return resultHtml 86 | } 87 | 88 | function serializeBody (): string { 89 | return ( 90 | document.body.innerHTML.replace(/)<[^<]*)*<\/script>/g, '') 91 | ) 92 | } 93 | 94 | function combineToSvg (node: Element, elBoundingClientRect: DOMRect): string { 95 | // Add Temporary identification classes 96 | addClasses(node) 97 | 98 | const documentHead = serializeHead(elBoundingClientRect) 99 | const documentBody = serializeBody() 100 | 101 | const offesetX = -elBoundingClientRect.left 102 | const offesetY = -elBoundingClientRect.top 103 | 104 | const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg') 105 | svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg') 106 | svg.setAttribute('height', elBoundingClientRect.height.toString()) 107 | svg.setAttribute('width', elBoundingClientRect.width.toString()) 108 | 109 | const foreignObject = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject') 110 | foreignObject.removeAttribute('xmlns') 111 | foreignObject.setAttribute('x', '0') // offesetX.toString()) 112 | foreignObject.setAttribute('y', '0') // offesetY.toString()) 113 | foreignObject.setAttribute('height', window.outerHeight.toString()) 114 | foreignObject.setAttribute('width', window.outerWidth.toString()) 115 | 116 | const innerHtml = document.createElement('div') 117 | innerHtml.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml') 118 | innerHtml.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink') 119 | innerHtml.setAttribute('class', document.body.className) 120 | 121 | // additional namespace for rendering 122 | innerHtml.innerHTML = documentBody 123 | 124 | foreignObject.appendChild(innerHtml) 125 | 126 | svg.insertAdjacentHTML('afterbegin', documentHead) 127 | svg.appendChild(foreignObject) 128 | 129 | // Remove Temporary identification classes 130 | removeClasses(node) 131 | 132 | const svgString = new XMLSerializer().serializeToString(svg) 133 | return svgString 134 | } 135 | 136 | export default function nodeToDataURL (userConfig: Partial): Promise { 137 | config = { ...defaultOptions, ...userConfig } 138 | 139 | const node = config.targetNode 140 | const elBoundingClientRect = node.getBoundingClientRect() 141 | const svg = combineToSvg(node, elBoundingClientRect) 142 | 143 | // Build base64 data url 144 | const svgBase64 = encodeURIComponent(svg) 145 | const dataURL = `data:image/svg+xml,${svgBase64}` 146 | 147 | const canvas = document.createElement('canvas') 148 | 149 | return new Promise((resolve, reject) => { 150 | if (!canvas) { 151 | reject() 152 | return 153 | } 154 | 155 | // Load data URL into a Image 156 | const img = document.createElement('img') 157 | 158 | // In case the generated dataURL in invalid 159 | img.addEventListener('error', () => { 160 | canvas.remove() 161 | reject() 162 | return 163 | }) 164 | 165 | // Wait for the image to be loaded, otherwise the buffer is empty 166 | img.addEventListener('load', () => { 167 | // For debugging feel free to use this fiddle 168 | // https://jsfiddle.net/j0asnubp/9/ 169 | // just paste in the base64URL 170 | canvas.width = elBoundingClientRect.width 171 | canvas.height = elBoundingClientRect.height 172 | 173 | const ctx = canvas.getContext('2d')! 174 | ctx.drawImage(img, 0, 0) 175 | 176 | resolve(canvas.toDataURL()) 177 | canvas.remove() 178 | }) 179 | 180 | // only attach the data URL after all eventHandler are attached 181 | img.src = dataURL 182 | }) 183 | } 184 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "ESNext", 5 | "outDir": "./dist", 6 | "strict": true, 7 | "esModuleInterop": true, 8 | "lib": [ 9 | "ESNext", 10 | "dom" 11 | ], 12 | "declaration": true, 13 | "declarationDir": "types", 14 | "typeRoots": ["types"] 15 | } 16 | } -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | export interface CaptureOptions { 2 | targetNode: HTMLElement | Element; 3 | excludedNodes: HTMLElement[] | Element[]; 4 | customStyle: string; 5 | returnType: string; 6 | } 7 | 8 | export default function nodeToDataURL(userConfig: Partial): Promise; 9 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | // vite.config.js 2 | import { resolve } from 'path' 3 | import { defineConfig } from 'vite' 4 | 5 | export default defineConfig({ 6 | build: { 7 | lib: { 8 | formats: ['cjs', 'es', 'umd'], 9 | entry: resolve(__dirname, 'src/index.ts'), 10 | name: 'nodeToDataURL', 11 | fileName: 'html-element-to-image', 12 | } 13 | }, 14 | }) --------------------------------------------------------------------------------