fix(mind-map): show text in links between nodes on export

This commit is contained in:
lzinga
2025-12-04 11:08:44 -08:00
parent b8585594cd
commit 5c9503732d
4 changed files with 36 additions and 5 deletions

View File

@@ -33,6 +33,7 @@
"@triliumnext/highlightjs": "workspace:*", "@triliumnext/highlightjs": "workspace:*",
"@triliumnext/share-theme": "workspace:*", "@triliumnext/share-theme": "workspace:*",
"@triliumnext/split.js": "workspace:*", "@triliumnext/split.js": "workspace:*",
"@zumer/snapdom": "2.0.1",
"autocomplete.js": "0.38.1", "autocomplete.js": "0.38.1",
"bootstrap": "5.3.8", "bootstrap": "5.3.8",
"boxicons": "2.1.4", "boxicons": "2.1.4",

View File

@@ -925,6 +925,7 @@ export default {
areObjectsEqual, areObjectsEqual,
copyHtmlToClipboard, copyHtmlToClipboard,
createImageSrcUrl, createImageSrcUrl,
triggerDownload,
downloadSvg, downloadSvg,
downloadSvgAsPng, downloadSvgAsPng,
compareVersions, compareVersions,

View File

@@ -11,6 +11,7 @@ import { useEditorSpacedUpdate, useNoteLabelBoolean, useSyncedRef, useTriliumEve
import { refToJQuerySelector } from "../react/react_utils"; import { refToJQuerySelector } from "../react/react_utils";
import utils from "../../services/utils"; import utils from "../../services/utils";
import { DISPLAYABLE_LOCALE_IDS } from "@triliumnext/commons"; import { DISPLAYABLE_LOCALE_IDS } from "@triliumnext/commons";
import { snapdom, SnapdomOptions } from "@zumer/snapdom";
const NEW_TOPIC_NAME = ""; const NEW_TOPIC_NAME = "";
@@ -45,11 +46,26 @@ export default function MindMap({ note, ntxId, noteContext }: TypeWidgetProps) {
const apiRef = useRef<MindElixirInstance>(null); const apiRef = useRef<MindElixirInstance>(null);
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const [ isReadOnly ] = useNoteLabelBoolean(note, "readOnly"); const [ isReadOnly ] = useNoteLabelBoolean(note, "readOnly");
// Shared options for snapdom screenshot generation used in both attachment saving and exports
const imageOptions : SnapdomOptions = {
backgroundColor: "transparent",
scale: 2
};
const spacedUpdate = useEditorSpacedUpdate({ const spacedUpdate = useEditorSpacedUpdate({
note, note,
noteContext, noteContext,
getData: async () => { getData: async () => {
if (!apiRef.current) return; if (!apiRef.current) return;
const result = await snapdom(apiRef.current.nodes, imageOptions);
// a data URL in the format: "data:image/svg+xml;charset=utf-8,<url-encoded-svg>"
// We need to extract the content after the comma and decode the URL encoding (%3C to <, %20 to space, etc.)
// to get raw SVG content that Trilium's backend can store as an attachment
const svgContent = decodeURIComponent(result.url.split(',')[1]);
return { return {
content: apiRef.current.getDataString(), content: apiRef.current.getDataString(),
attachments: [ attachments: [
@@ -57,7 +73,7 @@ export default function MindMap({ note, ntxId, noteContext }: TypeWidgetProps) {
role: "image", role: "image",
title: "mindmap-export.svg", title: "mindmap-export.svg",
mime: "image/svg+xml", mime: "image/svg+xml",
content: await apiRef.current.exportSvg().text(), content: svgContent,
position: 0 position: 0
} }
] ]
@@ -88,13 +104,18 @@ export default function MindMap({ note, ntxId, noteContext }: TypeWidgetProps) {
// Export as PNG or SVG. // Export as PNG or SVG.
useTriliumEvents([ "exportSvg", "exportPng" ], async ({ ntxId: eventNtxId }, eventName) => { useTriliumEvents([ "exportSvg", "exportPng" ], async ({ ntxId: eventNtxId }, eventName) => {
if (eventNtxId !== ntxId || !apiRef.current) return; if (eventNtxId !== ntxId || !apiRef.current) return;
const title = note.title;
const svg = await apiRef.current.exportSvg().text(); const result = await snapdom(apiRef.current.nodes, imageOptions);
let dataUrl;
if (eventName === "exportSvg") { if (eventName === "exportSvg") {
utils.downloadSvg(title, svg); dataUrl = result.url; // Native SVG Data URL
} else { } else {
utils.downloadSvgAsPng(title, svg); const pngImg = await result.toPng();
dataUrl = pngImg.src; // PNG Data URL
} }
await utils.triggerDownload(`${note.title}.${eventName === "exportSvg" ? "svg" : "png"}`, dataUrl);
}); });
const onKeyDown = useCallback((e: KeyboardEvent) => { const onKeyDown = useCallback((e: KeyboardEvent) => {

8
pnpm-lock.yaml generated
View File

@@ -211,6 +211,9 @@ importers:
'@triliumnext/split.js': '@triliumnext/split.js':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/splitjs version: link:../../packages/splitjs
'@zumer/snapdom':
specifier: 2.0.1
version: 2.0.1
autocomplete.js: autocomplete.js:
specifier: 0.38.1 specifier: 0.38.1
version: 0.38.1 version: 0.38.1
@@ -5754,6 +5757,9 @@ packages:
resolution: {integrity: sha512-PI6UdgpSeVoGvzguKHmy2bwOqI3UYkntLZOCpyJSKIi7234c5aJmQYkJB/P4P2YUJkqhbqvu7iM2/0eJZ178nA==} resolution: {integrity: sha512-PI6UdgpSeVoGvzguKHmy2bwOqI3UYkntLZOCpyJSKIi7234c5aJmQYkJB/P4P2YUJkqhbqvu7iM2/0eJZ178nA==}
engines: {bun: '>=0.7.0', deno: '>=1.0.0', node: '>=16.5.0'} engines: {bun: '>=0.7.0', deno: '>=1.0.0', node: '>=16.5.0'}
'@zumer/snapdom@2.0.1':
resolution: {integrity: sha512-78/qbYl2FTv4H6qaXcNfAujfIOSzdvs83NW63VbyC9QA3sqNPfPvhn4xYMO6Gy11hXwJUEhd0z65yKiNzDwy9w==}
abab@2.0.6: abab@2.0.6:
resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==}
deprecated: Use your platform's native atob() and btoa() methods instead deprecated: Use your platform's native atob() and btoa() methods instead
@@ -20671,6 +20677,8 @@ snapshots:
'@zip.js/zip.js@2.8.2': {} '@zip.js/zip.js@2.8.2': {}
'@zumer/snapdom@2.0.1': {}
abab@2.0.6: {} abab@2.0.6: {}
abbrev@1.1.1: {} abbrev@1.1.1: {}