Files
silverbullet/Library/Malys/MarkmapMindmap.md
2026-02-05 12:53:43 +00:00

362 lines
12 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
author: malys
description: MarkMap mindmap integration.
name: "Library/Malys/MarkmapMindmap"
tags: meta/library
share.uri: "https://github.com/malys/silverbullet-libraries/blob/main/src/MarkmapMindmap.md"
share.hash: 65e1843c
share.mode: pull
---
# Markmap mindmap
This library provides a way to preview your [MarkMap](https://markmap.js.org/) mind map in a panel while you are editing your space.
With MarkMap Preview, you can:
- Preview your slides without leaving the context of your space
- See how your slides look in real-time as you modify your markdown
- Use the MarkMap Preview panel to navigate through your slides and see them in action
![](https://community.silverbullet.md/uploads/default/original/2X/4/493e4db01035bc5d0ef5189f8f502ac7fc449709.gif)
## Disclaimer & Contributions
This code is provided **as-is**, **without any kind of support or warranty**.
I do **not** provide user support, bug-fixing on demand, or feature development.
If you detect a bug, please **actively participate in debugging it** (analysis, proposed fix, or pull request) **before reporting it**. Bug reports without investigation may be ignored.
🚫 **No new features will be added.**
**All types of contributions are welcome**, including bug fixes, refactoring, documentation improvements, and optimizations.
By using or contributing to this project, you acknowledge and accept these conditions.
## Configuration
To easily manage JS code source, we use a dynamic introspection mechanism based on md page path.
If you dont install program to default path `Library/Malys/xxx`,we have to set:
```lua
config.set("markmap.source ","xxxx")
-- ex: config.set("markmap.source ","Library/Malys/MarkmapMindmap")
-- where xxx is the path of MarkmapMindmap md page
```
> **warning** Caution
> **Depends on** [Utilities.md](https://github.com/malys/silverbullet-libraries/blob/main/src/Utilities.md). It will be installed automatically.
## Code
```space-lua
local source=config.get("markmap.source") or "Library/Malys/MarkmapMindmap"
local panelSize=config.get("markmap.panelSize") or 4
if library~=nil and (mls == nil or (mls ~=nil and mls.debug == nil)) then
library.install("https://github.com/malys/silverbullet-libraries/blob/main/src/Utilities.md")
editor.flashNotification("'Utilities' has been installed", "Info")
end
local function isVisible()
local current_panel_id = config.get("markmap.panelPosition") or "rhs"
local iframe = js.window.document.querySelector(".sb-panel."..current_panel_id..".is-expanded iframe")
if iframe and iframe.contentDocument then
local el = iframe.contentDocument.getElementById("mindmap")
if el then
return true
end
return false
end
end
local function show()
local page_content = editor.getText()
local contentBase64=encoding.base64Encode(page_content)
local content1= mls.getCodeBlock(source,"innerHTML","@CONTENT@", contentBase64)
local js = mls.getCodeBlock(source,"jsInject","@CONTENT@",content1)
local panel_html= mls.getCodeBlock(source,"template")
local current_panel_id = config.get("markmap.panelPosition") or "rhs"
editor.showPanel(current_panel_id,panelSize, panel_html, js)
end
local function hide()
local current_panel_id = config.get("markmap.panelPosition") or "rhs"
editor.hidePanel(current_panel_id)
end
local update = function(mode)
if ((not isVisible() and mode) or (not mode and isVisible())) then
show()
else
hide()
end
end
-- Define the command
command.define({
name = "Markmap: Toggle preview",
description = "Toggle Marp slides render in a panel",
run = function(e)
update(true)
end
})
event.listen({
name = "editor:pageSaved",
run = function(e)
update(false)
end
})
```
## JS templates
```js jsInject
const scriptId = "markmap-inject";
// Remove existing script if present
const existingScript = document.getElementById(scriptId);
if (existingScript) {
existingScript.remove();
}
// Create and inject the script again
const script = document.createElement("script");
script.type = "module";
script.id = scriptId;
script.textContent = `@CONTENT@`;
document.documentElement.appendChild(script);
```
```js innerHTML
function b64DecodeUnicode(str) {
return decodeURIComponent(
atob(str)
.split("")
.map(c => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2))
.join("")
);
}
import { Transformer } from "https://esm.sh/markmap-lib?bundle";
import { Markmap, deriveOptions } from "https://esm.sh/markmap-view?bundle";
import { Toolbar } from "https://esm.sh/markmap-toolbar?bundle";
const transformer = new Transformer();
const { root } = transformer.transform(b64DecodeUnicode("@CONTENT@"));
const svg = document.getElementById("mindmap");
const base = deriveOptions(null) || {};
const options = {
...base,
duration: 0,
autoFit: false
};
svg.innerHTML = "";
window.mm = Markmap.create(
"svg#mindmap",
options,
root
);
const existing = document.getElementsByClassName("mm-toolbar");
if(existing.length==0){
// 👉 Toolbar
const toolbar = new Toolbar();
toolbar.attach(window.mm);
const el = toolbar.render();
el.style.position = "absolute";
el.style.bottom = "20px";
el.style.right = "20px";
document.body.appendChild(el);
}
// 👉 print
window.addEventListener("beforeprint", () => {
window.mm?.fit();
});
```
## HTML template
```html template
<style>
html,
html {
overflow-y: scroll !important;
width: 90% !important;
}
@media print {
.no-print,
.mm-toolbar {
display: none !important;
}
.markmap-toolbar {
display: none !important;
}
.markmap-dark {
background: white !important;
color: black !important;
}
* {
animation: none !important;
transition: none !important;
}
/* Remove default page margins */
@page {
margin: 0;
}
/* 2⃣ Reset layout sizing */
html,
body {
margin: 0 !important;
padding: 0 !important;
height: auto !important;
overflow: visible !important;
}
/* Hide all toolbars */
.no-print,
.markmap-toolbar {
display: none !important;
}
}
body {
overflow: initial !important;
color: var(--top-color);
font-family: georgia, times, serif;
font-size: 13pt;
margin-top: revert;
margin-bottom: revert;
padding-left: 20px;
padding-right: 20px;
}
img {
max-width: 100%;
}
table {
width: 100%;
border-spacing: 0;
}
ul li p {
margin: 0;
}
thead tr {
background-color: var(--editor-table-head-background-color);
color: var(--editor-table-head-color);
}
th,
td {
padding: 8px;
}
tbody tr:nth-of-type(even) {
background-color: var(--editor-table-even-background-color);
}
a[href] {
text-decoration: none;
color: var(--link-color);
}
blockquote {
border-left: 1px solid var(--editor-blockquote-border-color);
margin-left: 2px;
padding-left: 10px;
background-color: var(--editor-blockquote-background-color);
color: var(--editor-blockquote-color);
}
hr {
margin: 1em 0 1em 0;
text-align: center;
border-color: var(--editor-ruler-color);
border-width: 0;
border-style: dotted;
}
hr:after {
content: "···";
letter-spacing: 1em;
}
span.highlight {
background-color: var(--highlight-color);
}
.markmap {
--markmap-a-color: #0097e6;
--markmap-a-hover-color: #00a8ff;
--markmap-highlight-bg: #ffeaa7;
--markmap-code-bg: #fff;
--markmap-code-color: #555;
--markmap-circle-open-bg: #fff;
--markmap-text-color: #333;
}
html[data-theme=dark] {
.markmap {
--markmap-code-bg: #1a1b26;
--markmap-code-color: #ddd;
--markmap-circle-open-bg: #444;
--markmap-text-color: #dddbdb;
}
}
* {
margin: 0;
padding: 0;
}
#mindmap {
display: block;
width: 100vw;
height: 100vh;
}
.mm-toolbar {display:flex;-webkit-user-select:none;-moz-user-select:none;user-select:none;align-items:center;border-width:1px;--un-border-opacity:1;border-color:rgb(212 212 216 / var(--un-border-opacity));border-radius:0.25rem;border-style:solid;--un-bg-opacity:1;background-color:rgb(255 255 255 / var(--un-bg-opacity));padding:0.25rem;line-height:1; }
.mm-toolbar:hover {--un-border-opacity:1;border-color:rgb(161 161 170 / var(--un-border-opacity)); }
.mm-toolbar svg {display:block; }
.mm-toolbar a {display:inline-block;text-decoration:none; }
.mm-toolbar-brand > img {width:1rem;height:1rem;vertical-align:middle; }
.mm-toolbar-brand > span {padding-left:0.25rem;padding-right:0.25rem; }
.mm-toolbar-brand:not(:first-child), .mm-toolbar-item:not(:first-child) {margin-left:0.25rem; }
.mm-toolbar-brand > *, .mm-toolbar-item > * {min-width:1rem;cursor:pointer;text-align:center;font-size:0.75rem;line-height:1rem;--un-text-opacity:1;color:rgb(161 161 170 / var(--un-text-opacity)); }
.mm-toolbar-brand.active,
.mm-toolbar-brand:hover,
.mm-toolbar-item.active,
.mm-toolbar-item:hover {border-radius:0.25rem;--un-bg-opacity:1;background-color:rgb(228 228 231 / var(--un-bg-opacity)); }
.mm-toolbar-brand.active > *, .mm-toolbar-brand:hover > *, .mm-toolbar-item.active > *, .mm-toolbar-item:hover > * {--un-text-opacity:1;color:rgb(39 39 42 / var(--un-text-opacity)); }
.mm-toolbar-brand.active, .mm-toolbar-item.active {--un-bg-opacity:1;background-color:rgb(212 212 216 / var(--un-bg-opacity)); }
.markmap-dark .mm-toolbar {--un-border-opacity:1;border-color:rgb(82 82 91 / var(--un-border-opacity));--un-bg-opacity:1;background-color:rgb(39 39 42 / var(--un-bg-opacity));--un-text-opacity:1;color:rgb(161 161 170 / var(--un-text-opacity)); }
.markmap-dark .mm-toolbar:hover {--un-border-opacity:1;border-color:rgb(113 113 122 / var(--un-border-opacity)); }
.markmap-dark .mm-toolbar > *:hover {--un-bg-opacity:1;background-color:rgb(82 82 91 / var(--un-bg-opacity)); }
.markmap-dark .mm-toolbar > *:hover > * {--un-text-opacity:1;color:rgb(228 228 231 / var(--un-text-opacity)); }
</style>
<div id="root" class="sb-preview">
<div class="sb-mindmap-toolbar no-print">
<button onclick="window.print()" title="Print">
🖨️
</button>
</div>
<svg id="mindmap"></svg>
</div>
```
## Changelog
* 2026-01-13: fix: multiple SVG visible on refresh
* 2025-12-01 fix: html duplicate inserts
## Community
[Silverbullet forum]([https://community.silverbullet.md/t/space-lua-addon-with-missing-git-commands-history-diff-restore/3539](https://community.silverbullet.md/t/mindmap-with-markmap-js/1556))