@ai-gui/core 0.19.0 → 0.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +47 -3
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +47 -3
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1738,6 +1738,44 @@ function measure(svg, options) {
|
|
|
1738
1738
|
};
|
|
1739
1739
|
}
|
|
1740
1740
|
/**
|
|
1741
|
+
* A copy of the drawing that a canvas will accept.
|
|
1742
|
+
*
|
|
1743
|
+
* The problem is `<foreignObject>`. Mermaid lays every node label out as HTML inside one, and a browser
|
|
1744
|
+
* taints the canvas the moment an image containing one is drawn — after which `toDataURL` throws
|
|
1745
|
+
* `SecurityError` and the export is lost. Nothing about the diagram is unsafe; the rule is categorical.
|
|
1746
|
+
*
|
|
1747
|
+
* So each `<foreignObject>` becomes plain SVG `<text>` at the same position, carrying the same words.
|
|
1748
|
+
* The result is a slightly plainer label — no wrapping, no HTML styling — and a diagram that exports at
|
|
1749
|
+
* all, which is the trade the alternative does not offer.
|
|
1750
|
+
*/
|
|
1751
|
+
function rasterisable(svg) {
|
|
1752
|
+
const copy = svg.cloneNode(true);
|
|
1753
|
+
const foreign = Array.from(copy.querySelectorAll("foreignObject"));
|
|
1754
|
+
for (const object of foreign) {
|
|
1755
|
+
const words = (object.textContent ?? "").trim();
|
|
1756
|
+
const parent = object.parentNode;
|
|
1757
|
+
if (!parent) continue;
|
|
1758
|
+
if (!words) {
|
|
1759
|
+
parent.removeChild(object);
|
|
1760
|
+
continue;
|
|
1761
|
+
}
|
|
1762
|
+
const x = Number.parseFloat(object.getAttribute("x") ?? "0");
|
|
1763
|
+
const y = Number.parseFloat(object.getAttribute("y") ?? "0");
|
|
1764
|
+
const width = Number.parseFloat(object.getAttribute("width") ?? "0");
|
|
1765
|
+
const height = Number.parseFloat(object.getAttribute("height") ?? "0");
|
|
1766
|
+
const text = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
|
1767
|
+
text.setAttribute("x", String(x + width / 2));
|
|
1768
|
+
text.setAttribute("y", String(y + height / 2));
|
|
1769
|
+
text.setAttribute("text-anchor", "middle");
|
|
1770
|
+
text.setAttribute("dominant-baseline", "middle");
|
|
1771
|
+
text.setAttribute("font-size", "14");
|
|
1772
|
+
text.setAttribute("fill", "currentColor");
|
|
1773
|
+
text.textContent = words;
|
|
1774
|
+
parent.replaceChild(text, object);
|
|
1775
|
+
}
|
|
1776
|
+
return copy;
|
|
1777
|
+
}
|
|
1778
|
+
/**
|
|
1741
1779
|
* Render an SVG element to a raster data URL.
|
|
1742
1780
|
*
|
|
1743
1781
|
* The element is serialized as it stands, so whatever a plugin drew — including the theme it drew
|
|
@@ -1746,7 +1784,7 @@ function measure(svg, options) {
|
|
|
1746
1784
|
async function exportSVGToImage(svg, options = {}) {
|
|
1747
1785
|
const { width, height } = measure(svg, options);
|
|
1748
1786
|
const scale = options.scale && options.scale > 0 ? options.scale : globalThis.devicePixelRatio || 1;
|
|
1749
|
-
const source = new XMLSerializer().serializeToString(svg);
|
|
1787
|
+
const source = new XMLSerializer().serializeToString(rasterisable(svg));
|
|
1750
1788
|
const namespaced = /\sxmlns=/.test(source) ? source : source.replace(/^<svg/, "<svg xmlns=\"http://www.w3.org/2000/svg\"");
|
|
1751
1789
|
const url = URL.createObjectURL(new Blob([namespaced], { type: "image/svg+xml;charset=utf-8" }));
|
|
1752
1790
|
try {
|
|
@@ -1779,9 +1817,15 @@ async function exportSVGToImage(svg, options = {}) {
|
|
|
1779
1817
|
* decide what lands in it.
|
|
1780
1818
|
*/
|
|
1781
1819
|
async function exportRenderedImages(root, options = {}) {
|
|
1782
|
-
const drawings = Array.from(root.querySelectorAll("svg"));
|
|
1783
1820
|
const exported = [];
|
|
1784
|
-
for (const drawing of
|
|
1821
|
+
for (const drawing of Array.from(root.querySelectorAll("svg"))) {
|
|
1822
|
+
if (drawing.closest(".katex")) continue;
|
|
1823
|
+
try {
|
|
1824
|
+
exported.push(await exportSVGToImage(drawing, options));
|
|
1825
|
+
} catch (reason) {
|
|
1826
|
+
options.onSkip?.(drawing, reason);
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1785
1829
|
return exported;
|
|
1786
1830
|
}
|
|
1787
1831
|
/** Save an exported image under the given file name. */
|
package/dist/index.d.cts
CHANGED
|
@@ -655,6 +655,14 @@ interface ExportImageOptions {
|
|
|
655
655
|
height?: number;
|
|
656
656
|
type?: "image/png" | "image/jpeg" | "image/webp";
|
|
657
657
|
quality?: number;
|
|
658
|
+
/**
|
|
659
|
+
* Told about each drawing that could not be exported, instead of the whole export failing.
|
|
660
|
+
*
|
|
661
|
+
* Some drawings cannot be rasterised at all: a Mermaid diagram lays its labels out in a
|
|
662
|
+
* `<foreignObject>`, and a browser taints the canvas the moment one is drawn onto it, so `toDataURL`
|
|
663
|
+
* throws `SecurityError`. One of those used to take every other drawing on the page down with it.
|
|
664
|
+
*/
|
|
665
|
+
onSkip?: (drawing: SVGElement, reason: unknown) => void;
|
|
658
666
|
}
|
|
659
667
|
interface ExportedImage {
|
|
660
668
|
dataUrl: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -655,6 +655,14 @@ interface ExportImageOptions {
|
|
|
655
655
|
height?: number;
|
|
656
656
|
type?: "image/png" | "image/jpeg" | "image/webp";
|
|
657
657
|
quality?: number;
|
|
658
|
+
/**
|
|
659
|
+
* Told about each drawing that could not be exported, instead of the whole export failing.
|
|
660
|
+
*
|
|
661
|
+
* Some drawings cannot be rasterised at all: a Mermaid diagram lays its labels out in a
|
|
662
|
+
* `<foreignObject>`, and a browser taints the canvas the moment one is drawn onto it, so `toDataURL`
|
|
663
|
+
* throws `SecurityError`. One of those used to take every other drawing on the page down with it.
|
|
664
|
+
*/
|
|
665
|
+
onSkip?: (drawing: SVGElement, reason: unknown) => void;
|
|
658
666
|
}
|
|
659
667
|
interface ExportedImage {
|
|
660
668
|
dataUrl: string;
|
package/dist/index.js
CHANGED
|
@@ -1714,6 +1714,44 @@ function measure(svg, options) {
|
|
|
1714
1714
|
};
|
|
1715
1715
|
}
|
|
1716
1716
|
/**
|
|
1717
|
+
* A copy of the drawing that a canvas will accept.
|
|
1718
|
+
*
|
|
1719
|
+
* The problem is `<foreignObject>`. Mermaid lays every node label out as HTML inside one, and a browser
|
|
1720
|
+
* taints the canvas the moment an image containing one is drawn — after which `toDataURL` throws
|
|
1721
|
+
* `SecurityError` and the export is lost. Nothing about the diagram is unsafe; the rule is categorical.
|
|
1722
|
+
*
|
|
1723
|
+
* So each `<foreignObject>` becomes plain SVG `<text>` at the same position, carrying the same words.
|
|
1724
|
+
* The result is a slightly plainer label — no wrapping, no HTML styling — and a diagram that exports at
|
|
1725
|
+
* all, which is the trade the alternative does not offer.
|
|
1726
|
+
*/
|
|
1727
|
+
function rasterisable(svg) {
|
|
1728
|
+
const copy = svg.cloneNode(true);
|
|
1729
|
+
const foreign = Array.from(copy.querySelectorAll("foreignObject"));
|
|
1730
|
+
for (const object of foreign) {
|
|
1731
|
+
const words = (object.textContent ?? "").trim();
|
|
1732
|
+
const parent = object.parentNode;
|
|
1733
|
+
if (!parent) continue;
|
|
1734
|
+
if (!words) {
|
|
1735
|
+
parent.removeChild(object);
|
|
1736
|
+
continue;
|
|
1737
|
+
}
|
|
1738
|
+
const x = Number.parseFloat(object.getAttribute("x") ?? "0");
|
|
1739
|
+
const y = Number.parseFloat(object.getAttribute("y") ?? "0");
|
|
1740
|
+
const width = Number.parseFloat(object.getAttribute("width") ?? "0");
|
|
1741
|
+
const height = Number.parseFloat(object.getAttribute("height") ?? "0");
|
|
1742
|
+
const text = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
|
1743
|
+
text.setAttribute("x", String(x + width / 2));
|
|
1744
|
+
text.setAttribute("y", String(y + height / 2));
|
|
1745
|
+
text.setAttribute("text-anchor", "middle");
|
|
1746
|
+
text.setAttribute("dominant-baseline", "middle");
|
|
1747
|
+
text.setAttribute("font-size", "14");
|
|
1748
|
+
text.setAttribute("fill", "currentColor");
|
|
1749
|
+
text.textContent = words;
|
|
1750
|
+
parent.replaceChild(text, object);
|
|
1751
|
+
}
|
|
1752
|
+
return copy;
|
|
1753
|
+
}
|
|
1754
|
+
/**
|
|
1717
1755
|
* Render an SVG element to a raster data URL.
|
|
1718
1756
|
*
|
|
1719
1757
|
* The element is serialized as it stands, so whatever a plugin drew — including the theme it drew
|
|
@@ -1722,7 +1760,7 @@ function measure(svg, options) {
|
|
|
1722
1760
|
async function exportSVGToImage(svg, options = {}) {
|
|
1723
1761
|
const { width, height } = measure(svg, options);
|
|
1724
1762
|
const scale = options.scale && options.scale > 0 ? options.scale : globalThis.devicePixelRatio || 1;
|
|
1725
|
-
const source = new XMLSerializer().serializeToString(svg);
|
|
1763
|
+
const source = new XMLSerializer().serializeToString(rasterisable(svg));
|
|
1726
1764
|
const namespaced = /\sxmlns=/.test(source) ? source : source.replace(/^<svg/, "<svg xmlns=\"http://www.w3.org/2000/svg\"");
|
|
1727
1765
|
const url = URL.createObjectURL(new Blob([namespaced], { type: "image/svg+xml;charset=utf-8" }));
|
|
1728
1766
|
try {
|
|
@@ -1755,9 +1793,15 @@ async function exportSVGToImage(svg, options = {}) {
|
|
|
1755
1793
|
* decide what lands in it.
|
|
1756
1794
|
*/
|
|
1757
1795
|
async function exportRenderedImages(root, options = {}) {
|
|
1758
|
-
const drawings = Array.from(root.querySelectorAll("svg"));
|
|
1759
1796
|
const exported = [];
|
|
1760
|
-
for (const drawing of
|
|
1797
|
+
for (const drawing of Array.from(root.querySelectorAll("svg"))) {
|
|
1798
|
+
if (drawing.closest(".katex")) continue;
|
|
1799
|
+
try {
|
|
1800
|
+
exported.push(await exportSVGToImage(drawing, options));
|
|
1801
|
+
} catch (reason) {
|
|
1802
|
+
options.onSkip?.(drawing, reason);
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1761
1805
|
return exported;
|
|
1762
1806
|
}
|
|
1763
1807
|
/** Save an exported image under the given file name. */
|
package/package.json
CHANGED