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

12 KiB
Raw Permalink Blame History

author, description, name, tags, share.uri, share.hash, share.mode
author description name tags share.uri share.hash share.mode
malys MarkMap mindmap integration. Library/Malys/MarkmapMindmap meta/library https://github.com/malys/silverbullet-libraries/blob/main/src/MarkmapMindmap.md 65e1843c pull

Markmap mindmap

This library provides a way to preview your MarkMap 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

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:

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. It will be installed automatically.

Code

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

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);
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

<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