@markdy/renderer-dom 0.8.26 → 0.8.27

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.
Files changed (2) hide show
  1. package/dist/index.js +95 -11
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -808,13 +808,14 @@ function ensureAnnotationStyles(doc) {
808
808
  doc.head.appendChild(style);
809
809
  }
810
810
  function positionForAnnotation(position, bounds, index) {
811
- const pad = 24;
811
+ const pad = 28;
812
+ const topPad = 68;
812
813
  const p = (position ?? "").toLowerCase();
813
- if (p.includes("top") && p.includes("right")) return { x: bounds.width - pad - 200, y: pad + index * 48 };
814
- if (p.includes("top") && p.includes("left")) return { x: pad, y: pad + index * 48 };
814
+ if (p.includes("top") && p.includes("right")) return { x: bounds.width - pad - 200, y: topPad + index * 48 };
815
+ if (p.includes("top") && p.includes("left")) return { x: pad, y: topPad + index * 48 };
815
816
  if (p.includes("bottom") && p.includes("right")) return { x: bounds.width - pad - 200, y: bounds.height - pad - 40 };
816
817
  if (p.includes("bottom") && p.includes("left")) return { x: pad, y: bounds.height - pad - 40 };
817
- return { x: bounds.width - pad - 200, y: pad + index * 48 };
818
+ return { x: bounds.width - pad - 200, y: topPad + index * 48 };
818
819
  }
819
820
  function mountAnnotations(layer, annotations, nodes, theme, bounds) {
820
821
  if (annotations.length === 0) return;
@@ -2110,11 +2111,16 @@ function createDiagram(opts) {
2110
2111
  const totalDurationMs = plan.duration * 1e3;
2111
2112
  const durationSeconds = plan.duration;
2112
2113
  const viewport = document.createElement("div");
2114
+ viewport.className = "markdy-viewport";
2113
2115
  Object.assign(viewport.style, {
2114
2116
  position: "relative",
2115
2117
  width: "100%",
2118
+ height: "100%",
2119
+ maxWidth: "100%",
2120
+ maxHeight: "100%",
2116
2121
  aspectRatio: `${plan.meta.width} / ${plan.meta.height}`,
2117
- overflow: "hidden"
2122
+ overflow: "hidden",
2123
+ boxSizing: "border-box"
2118
2124
  });
2119
2125
  container.appendChild(viewport);
2120
2126
  let progressEl = null;
@@ -2269,14 +2275,85 @@ function createDiagram(opts) {
2269
2275
  nodeEls.set(node.id, el);
2270
2276
  }
2271
2277
  let fitScale = 1;
2278
+ let sceneOffsetX = 0;
2279
+ let sceneOffsetY = 0;
2280
+ function computeContentBounds() {
2281
+ if (!plan.nodes || plan.nodes.length === 0) {
2282
+ return { minX: 0, minY: 0, maxX: plan.meta.width, maxY: plan.meta.height, width: plan.meta.width, height: plan.meta.height };
2283
+ }
2284
+ let minX = Infinity;
2285
+ let minY = Infinity;
2286
+ let maxX = -Infinity;
2287
+ let maxY = -Infinity;
2288
+ for (const node of plan.nodes) {
2289
+ minX = Math.min(minX, node.x);
2290
+ minY = Math.min(minY, node.y);
2291
+ maxX = Math.max(maxX, node.x + node.width);
2292
+ maxY = Math.max(maxY, node.y + node.height);
2293
+ }
2294
+ for (const gb of plan.groupBoundaries ?? []) {
2295
+ minX = Math.min(minX, gb.x);
2296
+ minY = Math.min(minY, gb.y);
2297
+ maxX = Math.max(maxX, gb.x + gb.width);
2298
+ maxY = Math.max(maxY, gb.y + gb.height);
2299
+ }
2300
+ for (const bus of plan.treeBuses ?? []) {
2301
+ minX = Math.min(minX, bus.parentX, ...bus.childXs.length ? bus.childXs : [bus.parentX]);
2302
+ minY = Math.min(minY, bus.parentY, bus.branchY, bus.childY);
2303
+ maxX = Math.max(maxX, bus.parentX, ...bus.childXs.length ? bus.childXs : [bus.parentX]);
2304
+ maxY = Math.max(maxY, bus.parentY, bus.branchY, bus.childY);
2305
+ }
2306
+ if (plan.title) {
2307
+ minY = Math.min(minY, 20);
2308
+ }
2309
+ const pad = 36;
2310
+ minX = Math.max(0, minX - pad);
2311
+ minY = Math.max(0, minY - pad);
2312
+ maxX = Math.min(plan.meta.width, maxX + pad);
2313
+ maxY = Math.min(plan.meta.height, maxY + pad);
2314
+ const width = Math.max(maxX - minX, 200);
2315
+ const height = Math.max(maxY - minY, 140);
2316
+ return { minX, minY, maxX, maxY, width, height };
2317
+ }
2272
2318
  function scaleScene() {
2273
- const width = viewport.clientWidth || plan.meta.width;
2274
- fitScale = width / plan.meta.width;
2319
+ const isBrowserLayout = (viewport.clientWidth || container.clientWidth || 0) > 0;
2320
+ if (!isBrowserLayout) {
2321
+ fitScale = 1;
2322
+ sceneOffsetX = 0;
2323
+ sceneOffsetY = 0;
2324
+ scene.style.left = "0px";
2325
+ scene.style.top = "0px";
2326
+ scene.style.transformOrigin = "0 0";
2327
+ scene.style.transform = "scale(1)";
2328
+ return;
2329
+ }
2330
+ const vWidth = viewport.clientWidth || container.clientWidth || plan.meta.width;
2331
+ const vHeight = viewport.clientHeight || container.clientHeight || vWidth * plan.meta.height / plan.meta.width;
2332
+ const canvasScaleX = vWidth / plan.meta.width;
2333
+ const canvasScaleY = vHeight / plan.meta.height;
2334
+ const baseCanvasScale = Math.min(canvasScaleX, canvasScaleY);
2335
+ const bounds = computeContentBounds();
2336
+ const contentScaleX = vWidth * 0.94 / bounds.width;
2337
+ const contentScaleY = vHeight * 0.94 / bounds.height;
2338
+ const optimalContentScale = Math.min(contentScaleX, contentScaleY);
2339
+ const chosenScale = Math.max(
2340
+ baseCanvasScale,
2341
+ Math.min(optimalContentScale, baseCanvasScale * 1.45)
2342
+ );
2343
+ fitScale = Number.isFinite(chosenScale) && chosenScale > 0 ? chosenScale : 1;
2344
+ const scaledWidth = plan.meta.width * fitScale;
2345
+ const scaledHeight = plan.meta.height * fitScale;
2346
+ sceneOffsetX = (vWidth - scaledWidth) / 2;
2347
+ sceneOffsetY = (vHeight - scaledHeight) / 2;
2348
+ scene.style.left = `${sceneOffsetX}px`;
2349
+ scene.style.top = `${sceneOffsetY}px`;
2350
+ scene.style.transformOrigin = "0 0";
2275
2351
  scene.style.transform = `scale(${fitScale})`;
2276
2352
  }
2277
2353
  scaleScene();
2278
2354
  const resizeObserver = typeof ResizeObserver === "function" ? new ResizeObserver(scaleScene) : null;
2279
2355
  resizeObserver?.observe(viewport);
2356
+ if (container !== viewport) resizeObserver?.observe(container);
2280
2357
  const edgeRuntimes = /* @__PURE__ */ new Map();
2281
2358
  const edgeSceneId = createEdgeSceneId();
2282
2359
  const sequenceAnims = plan.diagramType === "sequence" ? mountSequenceLayer(
@@ -2341,8 +2418,8 @@ function createDiagram(opts) {
2341
2418
  function handleViewportWheel(event) {
2342
2419
  event.preventDefault();
2343
2420
  const rect = viewport.getBoundingClientRect();
2344
- const pointerX = (event.clientX - rect.left) / fitScale;
2345
- const pointerY = (event.clientY - rect.top) / fitScale;
2421
+ const pointerX = (event.clientX - rect.left - sceneOffsetX) / fitScale;
2422
+ const pointerY = (event.clientY - rect.top - sceneOffsetY) / fitScale;
2346
2423
  const nextScale = Math.min(MAX_VIEWPORT_ZOOM, Math.max(MIN_VIEWPORT_ZOOM, viewportScale * Math.exp(-event.deltaY * VIEWPORT_ZOOM_STEP)));
2347
2424
  if (nextScale === viewportScale) return;
2348
2425
  const sceneX = (pointerX - viewportPanX) / viewportScale;
@@ -2809,6 +2886,8 @@ ${serializer2.serializeToString(clonedSvg)}`;
2809
2886
  clonedScene.style.transform = `scale(${scale})`;
2810
2887
  clonedScene.style.transformOrigin = "0 0";
2811
2888
  clonedScene.style.position = "relative";
2889
+ clonedScene.style.left = "0px";
2890
+ clonedScene.style.top = "0px";
2812
2891
  clonedScene.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
2813
2892
  if (options.transparentBackground) {
2814
2893
  clonedScene.style.background = "transparent";
@@ -2853,7 +2932,10 @@ async function exportDiagramAsPng(containerEl, options = {}) {
2853
2932
  const img = new Image();
2854
2933
  await new Promise((resolve, reject) => {
2855
2934
  img.onload = () => resolve();
2856
- img.onerror = () => reject(new Error("Failed to rasterize SVG into Image for PNG export"));
2935
+ img.onerror = () => {
2936
+ URL.revokeObjectURL(url);
2937
+ reject(new Error("Failed to rasterize SVG into Image for PNG export"));
2938
+ };
2857
2939
  img.src = url;
2858
2940
  });
2859
2941
  const width = img.naturalWidth || 800;
@@ -2925,7 +3007,9 @@ var DiagramPresentationController = class {
2925
3007
  this.diagram.setPlaybackRate(rate);
2926
3008
  }
2927
3009
  onKeyDown = (e) => {
2928
- if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) return;
3010
+ if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement || e.target instanceof HTMLSelectElement || e.target?.isContentEditable) {
3011
+ return;
3012
+ }
2929
3013
  switch (e.key) {
2930
3014
  case "ArrowRight":
2931
3015
  case "PageDown":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markdy/renderer-dom",
3
- "version": "0.8.26",
3
+ "version": "0.8.27",
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.26"
47
+ "@markdy/core": "0.8.27"
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.26"
54
+ "@markdy/stdlib-systems": "0.8.27"
55
55
  },
56
56
  "scripts": {
57
57
  "build": "tsup",