living-documentation 7.39.0 → 7.40.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.
@@ -48,15 +48,25 @@ async function _selectionToBlob() {
48
48
  const cropW = Math.ceil(br.x - tl.x + PAD * 2);
49
49
  const cropH = Math.ceil(br.y - tl.y + PAD * 2);
50
50
 
51
- // Temporarily deselect so highlights don't appear in the PNG
51
+ // Temporarily clear every selection state so interactive chrome (selected
52
+ // edge color, label resize handles, anchor handles) does not appear in PNGs.
52
53
  const savedNodeIds = [...st.selectedNodeIds];
54
+ const savedEdgeIds = [...(st.selectedEdgeIds || [])];
55
+ const savedNetworkNodeIds = st.network.getSelectedNodes();
56
+ const savedNetworkEdgeIds = st.network.getSelectedEdges();
53
57
  st.network.unselectAll();
54
58
  st.selectedNodeIds = [];
59
+ st.selectedEdgeIds = [];
60
+ st.exportingPng = true;
55
61
  st.network.redraw();
62
+ await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)));
56
63
 
57
64
  const visCanvas = document.querySelector('#vis-canvas canvas');
58
65
  if (!visCanvas) {
59
- st.network.selectNodes(savedNodeIds);
66
+ st.exportingPng = false;
67
+ st.selectedNodeIds = savedNodeIds;
68
+ st.selectedEdgeIds = savedEdgeIds;
69
+ st.network.setSelection({ nodes: savedNetworkNodeIds, edges: savedNetworkEdgeIds });
60
70
  return null;
61
71
  }
62
72
 
@@ -73,9 +83,10 @@ async function _selectionToBlob() {
73
83
  cropX * dpr, cropY * dpr, cropW * dpr, cropH * dpr,
74
84
  0, 0, out.width, out.height
75
85
  );
86
+ st.exportingPng = false;
76
87
 
77
88
  const blob = await new Promise((res) => out.toBlob(res, 'image/png'));
78
- return { blob, savedNodeIds };
89
+ return { blob, savedNodeIds, savedEdgeIds, savedNetworkNodeIds, savedNetworkEdgeIds };
79
90
  }
80
91
 
81
92
  // ── Copy selection as PNG (to clipboard) ─────────────────────────────────────
@@ -83,14 +94,16 @@ async function _selectionToBlob() {
83
94
  export async function copySelectionAsPng() {
84
95
  const result = await _selectionToBlob();
85
96
  if (!result) return;
86
- const { blob, savedNodeIds } = result;
97
+ const { blob, savedNodeIds, savedEdgeIds, savedNetworkNodeIds, savedNetworkEdgeIds } = result;
87
98
  try {
88
99
  await navigator.clipboard.write([new ClipboardItem({ 'image/png': blob })]);
89
100
  showToast(t('diagram.toast.png_copied'));
90
101
  } catch {
91
102
  showToast(t('diagram.toast.png_copy_error'), 'error');
92
103
  } finally {
93
- st.network.selectNodes(savedNodeIds);
104
+ st.selectedNodeIds = savedNodeIds;
105
+ st.selectedEdgeIds = savedEdgeIds;
106
+ st.network.setSelection({ nodes: savedNetworkNodeIds, edges: savedNetworkEdgeIds });
94
107
  }
95
108
  }
96
109
 
@@ -99,7 +112,7 @@ export async function copySelectionAsPng() {
99
112
  export async function saveSelectionAsPng(filename) {
100
113
  const result = await _selectionToBlob();
101
114
  if (!result) return;
102
- const { blob, savedNodeIds } = result;
115
+ const { blob, savedNodeIds, savedEdgeIds, savedNetworkNodeIds, savedNetworkEdgeIds } = result;
103
116
  try {
104
117
  const name = filename.replace(/\.[^.]+$/, ''); // strip extension
105
118
  await uploadImageBlob(blob, 'png', name);
@@ -107,7 +120,9 @@ export async function saveSelectionAsPng(filename) {
107
120
  } catch {
108
121
  showToast(t('diagram.toast.diagram_save_png_error'), 'error');
109
122
  } finally {
110
- st.network.selectNodes(savedNodeIds);
123
+ st.selectedNodeIds = savedNodeIds;
124
+ st.selectedEdgeIds = savedEdgeIds;
125
+ st.network.setSelection({ nodes: savedNetworkNodeIds, edges: savedNetworkEdgeIds });
111
126
  }
112
127
  }
113
128
 
@@ -679,6 +679,7 @@ function makeAnchorRenderer() {
679
679
  const r = 4;
680
680
  return {
681
681
  drawNode() {
682
+ if (st.exportingPng) return;
682
683
  if (!selected && !hover) return; // invisible at rest
683
684
  ctx.save();
684
685
  ctx.translate(x, y);
@@ -123,6 +123,7 @@ export function getNearestPort(nodeId, canvasPos) {
123
123
  * `highlightedPort` (if any) is rendered larger and fully orange.
124
124
  */
125
125
  export function drawPortDots(ctx, nodeId, highlightedPort) {
126
+ if (st.exportingPng) return;
126
127
  for (const key of PORT_KEYS) {
127
128
  const p = getPortPosition(nodeId, key);
128
129
  if (!p) continue;
@@ -383,4 +384,3 @@ function _distToSegment(p, a, b) {
383
384
  const t = Math.max(0, Math.min(1, ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2));
384
385
  return Math.hypot(p.x - (a.x + t * dx), p.y - (a.y + t * dy));
385
386
  }
386
-
@@ -31,6 +31,7 @@ export const st = {
31
31
  edgeLabelCanvasPos: {}, // edgeId → {x, y} canvas coords of last drawn label, used by label editor
32
32
  edgeLabelBBox: {}, // edgeId → {cx, cy, w, h, rotation} canvas world coords, used by label resize
33
33
  freeArrowFirstPoint: null, // addEdge two-click flow: {x, y} canvas coords of first click, or null
34
+ exportingPng: false, // suppress interactive-only canvas chrome during PNG export
34
35
  };
35
36
 
36
37
  export function markDirty() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "living-documentation",
3
- "version": "7.39.0",
3
+ "version": "7.40.0",
4
4
  "description": "A CLI tool that serves a local Markdown documentation viewer",
5
5
  "main": "dist/src/server.js",
6
6
  "bin": {