@markdy/renderer-dom 0.8.28 → 1.0.0

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.d.ts CHANGED
@@ -101,6 +101,11 @@ declare function exportDiagramAsGif(container: HTMLElement, timeline: TimelineCo
101
101
  * packages/renderer-dom/src/export/png-exporter.ts
102
102
  * High-DPI raster PNG export with 2x retina scaling.
103
103
  * Zero external dependencies.
104
+ *
105
+ * Fix: Inline all external resources (images, fonts) as base64 data URIs
106
+ * before drawing the SVG to canvas. A foreignObject-wrapped SVG taints the
107
+ * canvas whenever it references any external URL, so every resource must be
108
+ * inlined first.
104
109
  */
105
110
 
106
111
  interface PngExportOptions extends SvgExportOptions {
package/dist/index.js CHANGED
@@ -2939,10 +2939,74 @@ ${serializer2.serializeToString(clonedSvg)}`;
2939
2939
  ` + serializer.serializeToString(svg);
2940
2940
  }
2941
2941
 
2942
+ // src/export/inline-resources.ts
2943
+ async function toDataUri(url) {
2944
+ try {
2945
+ const resp = await fetch(url, { mode: "cors", credentials: "same-origin" });
2946
+ if (!resp.ok) return null;
2947
+ const arrayBuffer = await resp.arrayBuffer();
2948
+ const mimeType = resp.headers.get("Content-Type") || "application/octet-stream";
2949
+ const base64 = btoa(
2950
+ new Uint8Array(arrayBuffer).reduce((data, byte) => data + String.fromCharCode(byte), "")
2951
+ );
2952
+ return `data:${mimeType};base64,${base64}`;
2953
+ } catch {
2954
+ return null;
2955
+ }
2956
+ }
2957
+ async function inlineCssUrls(cssValue) {
2958
+ const urlPattern = /url\(["']?([^"')]+)["']?\)/g;
2959
+ const matches = [];
2960
+ let m;
2961
+ while ((m = urlPattern.exec(cssValue)) !== null) {
2962
+ const src = m[1];
2963
+ if (!src.startsWith("data:")) {
2964
+ matches.push({ full: m[0], src });
2965
+ }
2966
+ }
2967
+ let result = cssValue;
2968
+ await Promise.all(
2969
+ matches.map(async ({ full, src }) => {
2970
+ const absoluteSrc = new URL(src, document.baseURI).href;
2971
+ const dataUri = await toDataUri(absoluteSrc);
2972
+ if (dataUri) result = result.split(full).join(`url("${dataUri}")`);
2973
+ })
2974
+ );
2975
+ return result;
2976
+ }
2977
+ async function inlineExternalResources(root) {
2978
+ const tasks = [];
2979
+ root.querySelectorAll("img").forEach((img) => {
2980
+ const src = img.getAttribute("src");
2981
+ if (src && !src.startsWith("data:")) {
2982
+ const absoluteSrc = new URL(src, document.baseURI).href;
2983
+ tasks.push(
2984
+ toDataUri(absoluteSrc).then((dataUri) => {
2985
+ if (dataUri) img.setAttribute("src", dataUri);
2986
+ })
2987
+ );
2988
+ }
2989
+ });
2990
+ const allEls = [root, ...Array.from(root.querySelectorAll("*"))];
2991
+ allEls.forEach((el) => {
2992
+ const style = el.getAttribute("style");
2993
+ if (style && style.includes("url(")) {
2994
+ tasks.push(
2995
+ inlineCssUrls(style).then((inlined) => {
2996
+ if (inlined !== style) el.setAttribute("style", inlined);
2997
+ })
2998
+ );
2999
+ }
3000
+ });
3001
+ await Promise.all(tasks);
3002
+ }
3003
+
2942
3004
  // src/export/gif-exporter.ts
2943
3005
  var nextFrame = () => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(() => resolve())));
2944
3006
  async function rasterizeFrame(container, pixelRatio, options) {
2945
- const svg = exportDiagramAsVectorSvg(container, options);
3007
+ const cloned = container.cloneNode(true);
3008
+ await inlineExternalResources(cloned);
3009
+ const svg = exportDiagramAsVectorSvg(cloned, options);
2946
3010
  const url = URL.createObjectURL(new Blob([svg], { type: "image/svg+xml;charset=utf-8" }));
2947
3011
  try {
2948
3012
  const image = new Image();
@@ -2995,8 +3059,10 @@ async function exportDiagramAsGif(container, timeline, options = {}) {
2995
3059
 
2996
3060
  // src/export/png-exporter.ts
2997
3061
  async function exportDiagramAsPng(containerEl, options = {}) {
2998
- const svgXml = exportDiagramAsVectorSvg(containerEl, options);
2999
3062
  const pixelRatio = options.pixelRatio || 2;
3063
+ const clonedContainer = containerEl.cloneNode(true);
3064
+ await inlineExternalResources(clonedContainer);
3065
+ const svgXml = exportDiagramAsVectorSvg(clonedContainer, options);
3000
3066
  const blob = new Blob([svgXml], { type: "image/svg+xml;charset=utf-8" });
3001
3067
  const url = URL.createObjectURL(blob);
3002
3068
  const img = new Image();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markdy/renderer-dom",
3
- "version": "0.8.28",
3
+ "version": "1.0.0",
4
4
  "description": "Browser renderer for diagram-native animated MarkdyScript architecture diagrams, built on the Web Animations API.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -44,14 +44,14 @@
44
44
  "access": "public"
45
45
  },
46
46
  "dependencies": {
47
- "@markdy/core": "0.8.28"
47
+ "@markdy/core": "1.0.0"
48
48
  },
49
49
  "devDependencies": {
50
50
  "jsdom": "^29.1.1",
51
51
  "tsup": "^8.5.1",
52
52
  "typescript": "^5.9.3",
53
53
  "vitest": "^4.1.7",
54
- "@markdy/stdlib-systems": "0.8.28"
54
+ "@markdy/stdlib-systems": "1.0.0"
55
55
  },
56
56
  "scripts": {
57
57
  "build": "tsup",