mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-03 20:06:08 +01:00 
			
		
		
		
	Modularize proof of concept
This commit is contained in:
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,2 +1,4 @@
 | 
			
		||||
node_modules/
 | 
			
		||||
legacy/
 | 
			
		||||
dist/
 | 
			
		||||
.env
 | 
			
		||||
							
								
								
									
										10
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -15,6 +15,7 @@
 | 
			
		||||
        "dotenv": "^16.3.1",
 | 
			
		||||
        "esbuild": "^0.19.3",
 | 
			
		||||
        "eslint": "^8.49.0",
 | 
			
		||||
        "highlight.js": "^11.8.0",
 | 
			
		||||
        "trilium-etapi": "^0.1.2",
 | 
			
		||||
        "typescript": "^5.2.2"
 | 
			
		||||
      }
 | 
			
		||||
@@ -1811,6 +1812,15 @@
 | 
			
		||||
        "node": ">=8"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/highlight.js": {
 | 
			
		||||
      "version": "11.8.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.8.0.tgz",
 | 
			
		||||
      "integrity": "sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12.0.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/ignore": {
 | 
			
		||||
      "version": "5.2.4",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,9 @@
 | 
			
		||||
  "description": "",
 | 
			
		||||
  "main": "index.js",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "build": "esrun scripts/build.ts",
 | 
			
		||||
    "build-main": "esrun scripts/build.ts -- --module=main",
 | 
			
		||||
    "build-styles": "esrun scripts/build.ts -- --module=styles",
 | 
			
		||||
    "test": "echo \"Error: no test specified\" && exit 1"
 | 
			
		||||
  },
 | 
			
		||||
  "author": "",
 | 
			
		||||
@@ -15,6 +18,7 @@
 | 
			
		||||
    "dotenv": "^16.3.1",
 | 
			
		||||
    "esbuild": "^0.19.3",
 | 
			
		||||
    "eslint": "^8.49.0",
 | 
			
		||||
    "highlight.js": "^11.8.0",
 | 
			
		||||
    "trilium-etapi": "^0.1.2",
 | 
			
		||||
    "typescript": "^5.2.2"
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										31
									
								
								src/main/breadcrumbs.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/main/breadcrumbs.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
export default function buildBreadcrumbsFromNav() {
 | 
			
		||||
    const container = document.createElement("ul");
 | 
			
		||||
    container.id = "breadcrumbs";
 | 
			
		||||
    
 | 
			
		||||
    const current = document.querySelector("#menu .active");
 | 
			
		||||
    if (!current) return; // Something went really wrong
 | 
			
		||||
    const wrap = document.createElement("li");
 | 
			
		||||
    wrap.append(current.cloneNode(true));
 | 
			
		||||
    container.prepend(wrap);
 | 
			
		||||
    let next = current.closest("ul");
 | 
			
		||||
    while (next) {
 | 
			
		||||
        const clone = next?.previousElementSibling?.querySelector("a")?.cloneNode(true);
 | 
			
		||||
        if (!clone) continue; // This also means something went very wrong
 | 
			
		||||
        const wrap = document.createElement("li");
 | 
			
		||||
        wrap.append(clone);
 | 
			
		||||
        container.prepend(wrap);
 | 
			
		||||
        next = next?.parentElement?.closest("ul") ?? null;
 | 
			
		||||
        if (!next) {
 | 
			
		||||
            clone.textContent = "";
 | 
			
		||||
            const logo = document.createElement("img");
 | 
			
		||||
            logo.src = "https://raw.githubusercontent.com/zadam/trilium/master/images/icon-black.svg";
 | 
			
		||||
            clone.appendChild(logo);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // We don't need this at root
 | 
			
		||||
    if (container.children.length === 1) return;
 | 
			
		||||
    
 | 
			
		||||
    const main = document.getElementById("main");
 | 
			
		||||
    main?.prepend(container);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								src/main/externallinks.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/main/externallinks.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
export default function addExternalLinks() {
 | 
			
		||||
    const mapping = {
 | 
			
		||||
        EGFtX8Uw96FQ: "https://github.com/zadam/trilium"
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    for (const id in mapping) {
 | 
			
		||||
        const links = document.querySelectorAll<HTMLAnchorElement>(`a[href*="${id}"]`);
 | 
			
		||||
        if (!links.length) {console.warn(`Could not find link to note id ${id}`); continue;}
 | 
			
		||||
        for (const link of links) {
 | 
			
		||||
            link.href = mapping[id as keyof typeof mapping];
 | 
			
		||||
            link.target = "_blank";
 | 
			
		||||
            link.rel = "noopener noreferrer";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								src/main/fixactivelink.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/main/fixactivelink.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
export default function fixActiveLink() {
 | 
			
		||||
    const active = document.querySelector("#menu strong");
 | 
			
		||||
    if (!active) return; // Something is really wrong
 | 
			
		||||
    const link = document.createElement("a");
 | 
			
		||||
    link.className = "type-text active";
 | 
			
		||||
    link.href = ``;
 | 
			
		||||
    link.textContent = active.textContent;
 | 
			
		||||
    active.replaceWith(link);
 | 
			
		||||
    const id = document.body.dataset.noteId;
 | 
			
		||||
    link.href = `./${id}`;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								src/main/fixtableheaders.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/main/fixtableheaders.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
export default function fixTableHeaders() {
 | 
			
		||||
    Array.from(document.querySelectorAll("th")).forEach(el => {
 | 
			
		||||
        if (!el.textContent?.trim()) el.classList.add("empty");
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										31
									
								
								src/main/highlight.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/main/highlight.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
import {HLJSApi} from "highlight.js";
 | 
			
		||||
 | 
			
		||||
declare const hljs: HLJSApi;
 | 
			
		||||
 | 
			
		||||
export default function addHljs() {
 | 
			
		||||
    const link = document.createElement("link");
 | 
			
		||||
    link.rel = "stylesheet";
 | 
			
		||||
    link.href = "api/notes/cVaK9ZJwx5Hs/download";
 | 
			
		||||
    document.head.append(link);
 | 
			
		||||
 | 
			
		||||
    const script = document.createElement("script");
 | 
			
		||||
    script.src = "api/notes/6PVElIem02b5/download";
 | 
			
		||||
    script.addEventListener("load", () => {
 | 
			
		||||
        hljs.registerAliases(["application-javascript-env-frontend", "application-javascript-env-backend"], {languageName: "javascript"});
 | 
			
		||||
        hljs.addPlugin({
 | 
			
		||||
            "after:highlight": (result) => {
 | 
			
		||||
                // Add api global
 | 
			
		||||
                result.value = result.value.replaceAll(/([^A-Za-z0-9])api\./g, function(match, prefix) {
 | 
			
		||||
                    return `${prefix}<span class="hljs-variable language_">api</span>.`;
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                // Add jquery global
 | 
			
		||||
                result.value = result.value.replaceAll(/([^A-Za-z0-9\.])\$\((.+)\)/g, function(match, prefix, variable) {
 | 
			
		||||
                    return `${prefix}<span class="hljs-variable language_">$(</span>${variable}<span class="hljs-variable language_">)</span>`;
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        hljs.highlightAll();
 | 
			
		||||
    });
 | 
			
		||||
    document.head.append(script);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,312 +1,22 @@
 | 
			
		||||
import fixActiveLink from "./fixactivelink";
 | 
			
		||||
import fixTableHeaders from "./fixtableheaders";
 | 
			
		||||
import highlight from "./highlight";
 | 
			
		||||
import buildSidenav from "./sidenav";
 | 
			
		||||
import buildBreadcrumbs from "./breadcrumbs";
 | 
			
		||||
import fixSubMenu from "./submenu";
 | 
			
		||||
import generateTOC from "./toc";
 | 
			
		||||
import addExternalLinks from "./externallinks";
 | 
			
		||||
 | 
			
		||||
// https://instance-name/api/notes/vW1cXaYNN7OM/download
 | 
			
		||||
 | 
			
		||||
function addHljs() {
 | 
			
		||||
    const link = document.createElement("link");
 | 
			
		||||
    link.rel = "stylesheet";
 | 
			
		||||
    link.href = "api/notes/cVaK9ZJwx5Hs/download";
 | 
			
		||||
    document.head.append(link);
 | 
			
		||||
 | 
			
		||||
    const script = document.createElement("script");
 | 
			
		||||
    script.src = "api/notes/6PVElIem02b5/download";
 | 
			
		||||
    script.addEventListener("load", () => {
 | 
			
		||||
        hljs.registerAliases(["application-javascript-env-frontend", "application-javascript-env-backend"], {languageName: "javascript"});
 | 
			
		||||
        hljs.addPlugin({
 | 
			
		||||
            "after:highlight": (result) => {
 | 
			
		||||
                // Add api global
 | 
			
		||||
                result.value = result.value.replaceAll(/([^A-Za-z0-9])api\./g, function(match, prefix) {
 | 
			
		||||
                    return `${prefix}<span class="hljs-variable language_">api</span>.`;
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                // Add jquery global
 | 
			
		||||
                result.value = result.value.replaceAll(/([^A-Za-z0-9\.])\$\((.+)\)/g, function(match, prefix, variable) {
 | 
			
		||||
                    return `${prefix}<span class="hljs-variable language_">$(</span>${variable}<span class="hljs-variable language_">)</span>`;
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        hljs.highlightAll();
 | 
			
		||||
    });
 | 
			
		||||
    document.head.append(script);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function fixTableHeaders() {
 | 
			
		||||
    Array.from(document.querySelectorAll("th")).forEach(el => {
 | 
			
		||||
        if (!el.textContent.trim()) el.classList.add("empty");
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*Array.from(document.querySelectorAll("pre code")).forEach(el => {
 | 
			
		||||
    if (el.className.includes("javascript-env")) el.className = "language-javascript";
 | 
			
		||||
});*/
 | 
			
		||||
 | 
			
		||||
function addLogo() {
 | 
			
		||||
    const logo = document.createElement("img");
 | 
			
		||||
    logo.src = "https://raw.githubusercontent.com/zadam/trilium/master/images/icon-color.svg";
 | 
			
		||||
    logo.id = "logo";
 | 
			
		||||
    document.querySelector("#menu > p").append(logo);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function fixActiveLink() {
 | 
			
		||||
    const active = document.querySelector("#menu strong");
 | 
			
		||||
    const link = document.createElement("a");
 | 
			
		||||
    link.className = "type-text active";
 | 
			
		||||
    link.href = ``;
 | 
			
		||||
    link.textContent = active.textContent;
 | 
			
		||||
    active.replaceWith(link);
 | 
			
		||||
    const id = document.body.dataset.noteId;
 | 
			
		||||
    link.href = `./${id}`;
 | 
			
		||||
    
 | 
			
		||||
    /*fetchNote().then(note => {
 | 
			
		||||
        link.href = `./${note.noteId}`;
 | 
			
		||||
    });*/
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function addSideNav() {
 | 
			
		||||
    const categories = document.querySelectorAll("#menu > ul > li > ul > li");
 | 
			
		||||
    for (const cat of categories) cat.classList.add("category");
 | 
			
		||||
    const active = document.querySelector("#menu .active");
 | 
			
		||||
    const treeToClone = active.closest(".category.submenu-item") ?? active.closest(".submenu-item.hidden");
 | 
			
		||||
    if (!treeToClone) return; // probably homepage
 | 
			
		||||
    const layout = document.querySelector("#layout");
 | 
			
		||||
    const sidebar = document.createElement("ul");
 | 
			
		||||
    sidebar.id = "sidebar";
 | 
			
		||||
    const clone = treeToClone.cloneNode(true);
 | 
			
		||||
    /*const title = document.createElement("div");
 | 
			
		||||
    title.className = "title";
 | 
			
		||||
    title.append(clone.querySelector("p > a"));
 | 
			
		||||
    sidebar.append(title);
 | 
			
		||||
    sidebar.append(clone.querySelector("ul"));*/
 | 
			
		||||
    sidebar.append(clone);
 | 
			
		||||
    if (sidebar.querySelectorAll("li").length <= 1) return;
 | 
			
		||||
    layout.prepend(sidebar);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async function buildBreadcrumbs() {
 | 
			
		||||
    const main = document.getElementById("main");
 | 
			
		||||
    const placeholder = document.createElement("div");
 | 
			
		||||
    placeholder.id = "breadcrumbs";
 | 
			
		||||
    const pspan = document.createElement("span");
 | 
			
		||||
    const plink = document.createElement("a");
 | 
			
		||||
    pspan.append(plink);
 | 
			
		||||
    plink.href = "#";
 | 
			
		||||
    plink.textContent = document.getElementById("title").textContent;
 | 
			
		||||
    placeholder.append(pspan);
 | 
			
		||||
    main.prepend(placeholder);
 | 
			
		||||
    
 | 
			
		||||
    const container = document.createElement("div");
 | 
			
		||||
    container.id = "breadcrumbs";
 | 
			
		||||
 | 
			
		||||
    // const notePath = [];
 | 
			
		||||
    
 | 
			
		||||
    let currentNote, parentId;
 | 
			
		||||
    do {
 | 
			
		||||
        currentNote = await fetchNote(parentId);
 | 
			
		||||
        const span = document.createElement("span");
 | 
			
		||||
        const link = document.createElement("a");
 | 
			
		||||
        link.className = "type-text";
 | 
			
		||||
        link.href = `./${currentNote.noteId}`;
 | 
			
		||||
        link.textContent = currentNote.title;
 | 
			
		||||
        // notePath.splice(0, 0, link);
 | 
			
		||||
        span.append(link);
 | 
			
		||||
        container.prepend(span)
 | 
			
		||||
        parentId = currentNote.parentNoteIds[0];
 | 
			
		||||
        if (parentId === "_share") {
 | 
			
		||||
            link.textContent = "";
 | 
			
		||||
            const logo = document.createElement("img");
 | 
			
		||||
            logo.src = "https://raw.githubusercontent.com/zadam/trilium/master/images/icon-black.svg";
 | 
			
		||||
            link.append(logo);
 | 
			
		||||
        }
 | 
			
		||||
    } while(parentId !== "_share")
 | 
			
		||||
        
 | 
			
		||||
    if (container.children.length === 1) return;
 | 
			
		||||
    
 | 
			
		||||
    placeholder.replaceWith(container);
 | 
			
		||||
    /*let currentNote = await fetchNote();
 | 
			
		||||
    let parentId = currentNote.parentNoteIds[0];
 | 
			
		||||
    while (parentId !== "_share") {
 | 
			
		||||
        notePath.splice(0, 0, currentNote.title);
 | 
			
		||||
        parentId = currentNote.parentNoteIds[0];
 | 
			
		||||
        currentNote = await fetchNote(parentId);
 | 
			
		||||
    }*/
 | 
			
		||||
    
 | 
			
		||||
    // console.log(notePath);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function buildBreadcrumbsFromNav() {
 | 
			
		||||
    const container = document.createElement("ul");
 | 
			
		||||
    container.id = "breadcrumbs";
 | 
			
		||||
    
 | 
			
		||||
    const current = document.querySelector("#menu .active");
 | 
			
		||||
    const wrap = document.createElement("li");
 | 
			
		||||
    wrap.append(current.cloneNode(true));
 | 
			
		||||
    container.prepend(wrap);
 | 
			
		||||
    let next = current.closest("ul");
 | 
			
		||||
    while (next) {
 | 
			
		||||
        const clone = next.previousElementSibling.querySelector("a").cloneNode(true);
 | 
			
		||||
        const wrap = document.createElement("li");
 | 
			
		||||
        wrap.append(clone);
 | 
			
		||||
        container.prepend(wrap);
 | 
			
		||||
        next = next.parentElement.closest("ul");
 | 
			
		||||
        if (!next) {
 | 
			
		||||
            clone.textContent = "";
 | 
			
		||||
            const logo = document.createElement("img");
 | 
			
		||||
            logo.src = "https://raw.githubusercontent.com/zadam/trilium/master/images/icon-black.svg";
 | 
			
		||||
            clone.append(logo);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // We don't need this at root
 | 
			
		||||
    if (container.children.length === 1) return;
 | 
			
		||||
    
 | 
			
		||||
    const main = document.getElementById("main");
 | 
			
		||||
    main.prepend(container);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const submenuBlacklist = ["ZapIU17QNEyU"]
 | 
			
		||||
//if (item.innerHTML.includes(submenuBlacklist[0])) item.className += " hidden";
 | 
			
		||||
/*function fixSubMenu() {
 | 
			
		||||
    const items = document.querySelectorAll("#menu > ul > li");
 | 
			
		||||
    for (const item of items) {
 | 
			
		||||
        const sublist = item.querySelector("ul");
 | 
			
		||||
        if (sublist) {
 | 
			
		||||
            if (sublist.children.length) {
 | 
			
		||||
                item.className = "submenu";
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                sublist.remove();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}*/
 | 
			
		||||
 | 
			
		||||
function fixSubMenu() {
 | 
			
		||||
    const items = document.querySelectorAll("#menu ul li");
 | 
			
		||||
    for (const item of items) {
 | 
			
		||||
        const sublist = item.querySelector("ul");
 | 
			
		||||
        if (sublist) {
 | 
			
		||||
            if (sublist.children.length) {
 | 
			
		||||
                const ihtml = item.innerHTML;
 | 
			
		||||
                for (const bl of submenuBlacklist) {
 | 
			
		||||
                    if (!ihtml.includes(bl)) continue;
 | 
			
		||||
                    item.classList.add("hidden");
 | 
			
		||||
                }
 | 
			
		||||
                item.classList.add("submenu-item");
 | 
			
		||||
                sublist.classList.add("submenu");
 | 
			
		||||
                if (sublist.querySelector("ul")?.children.length) sublist.classList.add("hasSubmenu");
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                sublist.remove();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function generateTOC() {
 | 
			
		||||
    const slugify = text => text.toLowerCase().replace(/[^\w]/g, "-");
 | 
			
		||||
    const buildItem = (heading) => {
 | 
			
		||||
        const slug = slugify(heading.textContent);
 | 
			
		||||
 | 
			
		||||
        const anchor = document.createElement("a");
 | 
			
		||||
        anchor.setAttribute("href", `#${slug}`);
 | 
			
		||||
        anchor.setAttribute("name", slug);
 | 
			
		||||
        anchor.setAttribute("id", slug);
 | 
			
		||||
        anchor.textContent = "#";
 | 
			
		||||
 | 
			
		||||
        const link = document.createElement("a");
 | 
			
		||||
        link.setAttribute("href", `#${slug}`);
 | 
			
		||||
        link.textContent = heading.textContent;
 | 
			
		||||
 | 
			
		||||
        heading.append(anchor);
 | 
			
		||||
 | 
			
		||||
        const li = document.createElement("li");
 | 
			
		||||
        li.append(link);
 | 
			
		||||
        return li;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const headings = Array.from(document.querySelectorAll("h1, h2, h3, h4, h5, h6"));
 | 
			
		||||
    const items = headings.map(h => buildItem(h));
 | 
			
		||||
    if (headings.length <= 1) return;
 | 
			
		||||
 | 
			
		||||
    const getNum = el => parseInt(el.tagName.replace("H","").replace("h",""));
 | 
			
		||||
 | 
			
		||||
    const toc = document.createElement("ul");
 | 
			
		||||
    toc.id = "toc";
 | 
			
		||||
    const first = headings[1];
 | 
			
		||||
    const firstDepth = getNum(first);
 | 
			
		||||
 | 
			
		||||
    for (let h = 0; h < headings.length; h++) {
 | 
			
		||||
        const current = headings[h];
 | 
			
		||||
        const currentDepth = getNum(current);
 | 
			
		||||
        if (currentDepth === firstDepth) toc.append(items[h]);
 | 
			
		||||
 | 
			
		||||
        let nextIndex = h + 1;
 | 
			
		||||
        if (nextIndex >= headings.length) continue;
 | 
			
		||||
 | 
			
		||||
        const children = [];
 | 
			
		||||
        const childDepth = currentDepth + 1;
 | 
			
		||||
        let nextDepth = getNum(headings[nextIndex]);
 | 
			
		||||
        while (nextDepth > currentDepth) {
 | 
			
		||||
            if (nextDepth === childDepth) children.push(nextIndex);
 | 
			
		||||
            nextIndex++;
 | 
			
		||||
            if (nextIndex < headings.length) nextDepth = getNum(headings[nextIndex]);
 | 
			
		||||
            else nextDepth = currentDepth;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (children.length) {
 | 
			
		||||
            const ul = document.createElement("ul");
 | 
			
		||||
            for (const c of children) ul.append(items[c]);
 | 
			
		||||
            items[h].append(ul);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const sections = headings.slice(1);
 | 
			
		||||
    const links = toc.querySelectorAll("a");
 | 
			
		||||
    function changeLinkState() {
 | 
			
		||||
        let index = sections.length;
 | 
			
		||||
 | 
			
		||||
        while(--index && window.scrollY + 50 < sections[index].offsetTop) {}
 | 
			
		||||
 | 
			
		||||
        links.forEach((link) => link.classList.remove('active'));
 | 
			
		||||
        links[index].classList.add('active');
 | 
			
		||||
    }
 | 
			
		||||
      
 | 
			
		||||
    changeLinkState();
 | 
			
		||||
    window.addEventListener('scroll', changeLinkState);
 | 
			
		||||
 | 
			
		||||
    const layout = document.querySelector("#layout");
 | 
			
		||||
    layout.classList.add("toc");
 | 
			
		||||
    layout.append(toc)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function addExternalLinks() {
 | 
			
		||||
    const mapping = {
 | 
			
		||||
        EGFtX8Uw96FQ: "https://github.com/zadam/trilium"
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    for (const id in mapping) {
 | 
			
		||||
        const links = document.querySelectorAll(`a[href*="${id}"]`);
 | 
			
		||||
        if (!links.length) {console.warn(`Could not find link to note id ${id}`); continue;}
 | 
			
		||||
        for (const link of links) {
 | 
			
		||||
            link.href = mapping[id];
 | 
			
		||||
            link.target = "_blank";
 | 
			
		||||
            link.rel = "noopener noreferrer";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
try{fixActiveLink();} catch(e){console.error(e)}
 | 
			
		||||
try{addHljs();} catch(e){console.error(e)}
 | 
			
		||||
try{highlight();} catch(e){console.error(e)}
 | 
			
		||||
try{fixTableHeaders();} catch(e){console.error(e)}
 | 
			
		||||
// try{addLogo();} catch{}
 | 
			
		||||
 | 
			
		||||
try{fixSubMenu();} catch(e){console.error(e)}
 | 
			
		||||
try{addSideNav();} catch(e){console.error(e)}
 | 
			
		||||
try{buildBreadcrumbsFromNav();} catch(e){console.error(e)}
 | 
			
		||||
try{buildSidenav();} catch(e){console.error(e)}
 | 
			
		||||
try{buildBreadcrumbs();} catch(e){console.error(e)}
 | 
			
		||||
try{generateTOC();} catch(e){console.error(e)}
 | 
			
		||||
try{addExternalLinks();} catch(e){console.error(e)}
 | 
			
		||||
							
								
								
									
										19
									
								
								src/main/sidenav.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/main/sidenav.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
export default function addSideNav() {
 | 
			
		||||
    const categories = document.querySelectorAll("#menu > ul > li > ul > li");
 | 
			
		||||
    for (const cat of categories) cat.classList.add("category");
 | 
			
		||||
    const active = document.querySelector("#menu .active");
 | 
			
		||||
    const treeToClone = active?.closest(".category.submenu-item") ?? active?.closest(".submenu-item.hidden");
 | 
			
		||||
    if (!treeToClone) return; // probably homepage
 | 
			
		||||
    const layout = document.querySelector("#layout");
 | 
			
		||||
    const sidebar = document.createElement("ul");
 | 
			
		||||
    sidebar.id = "sidebar";
 | 
			
		||||
    const clone = treeToClone.cloneNode(true);
 | 
			
		||||
    /*const title = document.createElement("div");
 | 
			
		||||
    title.className = "title";
 | 
			
		||||
    title.append(clone.querySelector("p > a"));
 | 
			
		||||
    sidebar.append(title);
 | 
			
		||||
    sidebar.append(clone.querySelector("ul"));*/
 | 
			
		||||
    sidebar.append(clone);
 | 
			
		||||
    if (sidebar.querySelectorAll("li").length <= 1) return;
 | 
			
		||||
    layout?.prepend(sidebar);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										38
									
								
								src/main/submenu.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/main/submenu.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
const submenuBlacklist = ["ZapIU17QNEyU"]
 | 
			
		||||
//if (item.innerHTML.includes(submenuBlacklist[0])) item.className += " hidden";
 | 
			
		||||
/*function fixSubMenu() {
 | 
			
		||||
    const items = document.querySelectorAll("#menu > ul > li");
 | 
			
		||||
    for (const item of items) {
 | 
			
		||||
        const sublist = item.querySelector("ul");
 | 
			
		||||
        if (sublist) {
 | 
			
		||||
            if (sublist.children.length) {
 | 
			
		||||
                item.className = "submenu";
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                sublist.remove();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}*/
 | 
			
		||||
 | 
			
		||||
export default function fixSubMenu() {
 | 
			
		||||
    const items = document.querySelectorAll("#menu ul li");
 | 
			
		||||
    for (const item of items) {
 | 
			
		||||
        const sublist = item.querySelector("ul");
 | 
			
		||||
        if (sublist) {
 | 
			
		||||
            if (sublist.children.length) {
 | 
			
		||||
                const ihtml = item.innerHTML;
 | 
			
		||||
                for (const bl of submenuBlacklist) {
 | 
			
		||||
                    if (!ihtml.includes(bl)) continue;
 | 
			
		||||
                    item.classList.add("hidden");
 | 
			
		||||
                }
 | 
			
		||||
                item.classList.add("submenu-item");
 | 
			
		||||
                sublist.classList.add("submenu");
 | 
			
		||||
                if (sublist.querySelector("ul")?.children.length) sublist.classList.add("hasSubmenu");
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                sublist.remove();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										76
									
								
								src/main/toc.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								src/main/toc.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,76 @@
 | 
			
		||||
export default function generateTOC() {
 | 
			
		||||
    const slugify = (text: string) => text.toLowerCase().replace(/[^\w]/g, "-");
 | 
			
		||||
    const buildItem = (heading: Element) => {
 | 
			
		||||
        const slug = slugify(heading.textContent ?? "");
 | 
			
		||||
 | 
			
		||||
        const anchor = document.createElement("a");
 | 
			
		||||
        anchor.setAttribute("href", `#${slug}`);
 | 
			
		||||
        anchor.setAttribute("name", slug);
 | 
			
		||||
        anchor.setAttribute("id", slug);
 | 
			
		||||
        anchor.textContent = "#";
 | 
			
		||||
 | 
			
		||||
        const link = document.createElement("a");
 | 
			
		||||
        link.setAttribute("href", `#${slug}`);
 | 
			
		||||
        link.textContent = heading.textContent;
 | 
			
		||||
 | 
			
		||||
        heading.append(anchor);
 | 
			
		||||
 | 
			
		||||
        const li = document.createElement("li");
 | 
			
		||||
        li.append(link);
 | 
			
		||||
        return li;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const headings = Array.from(document.querySelectorAll("h1, h2, h3, h4, h5, h6"));
 | 
			
		||||
    const items = headings.map(h => buildItem(h));
 | 
			
		||||
    if (headings.length <= 1) return;
 | 
			
		||||
 | 
			
		||||
    const getNum = (el: Element) => parseInt(el.tagName.replace("H","").replace("h",""));
 | 
			
		||||
 | 
			
		||||
    const toc = document.createElement("ul");
 | 
			
		||||
    toc.id = "toc";
 | 
			
		||||
    const first = headings[1];
 | 
			
		||||
    const firstDepth = getNum(first);
 | 
			
		||||
 | 
			
		||||
    for (let h = 0; h < headings.length; h++) {
 | 
			
		||||
        const current = headings[h];
 | 
			
		||||
        const currentDepth = getNum(current);
 | 
			
		||||
        if (currentDepth === firstDepth) toc.append(items[h]);
 | 
			
		||||
 | 
			
		||||
        let nextIndex = h + 1;
 | 
			
		||||
        if (nextIndex >= headings.length) continue;
 | 
			
		||||
 | 
			
		||||
        const children = [];
 | 
			
		||||
        const childDepth = currentDepth + 1;
 | 
			
		||||
        let nextDepth = getNum(headings[nextIndex]);
 | 
			
		||||
        while (nextDepth > currentDepth) {
 | 
			
		||||
            if (nextDepth === childDepth) children.push(nextIndex);
 | 
			
		||||
            nextIndex++;
 | 
			
		||||
            if (nextIndex < headings.length) nextDepth = getNum(headings[nextIndex]);
 | 
			
		||||
            else nextDepth = currentDepth;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (children.length) {
 | 
			
		||||
            const ul = document.createElement("ul");
 | 
			
		||||
            for (const c of children) ul.append(items[c]);
 | 
			
		||||
            items[h].append(ul);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const sections = headings.slice(1);
 | 
			
		||||
    const links = toc.querySelectorAll("a");
 | 
			
		||||
    function changeLinkState() {
 | 
			
		||||
        let index = sections.length;
 | 
			
		||||
 | 
			
		||||
        while(--index && window.scrollY + 50 < (sections[index] as HTMLElement).offsetTop) {}
 | 
			
		||||
 | 
			
		||||
        links.forEach((link) => link.classList.remove('active'));
 | 
			
		||||
        links[index].classList.add('active');
 | 
			
		||||
    }
 | 
			
		||||
      
 | 
			
		||||
    changeLinkState();
 | 
			
		||||
    window.addEventListener('scroll', changeLinkState);
 | 
			
		||||
 | 
			
		||||
    const layout = document.querySelector("#layout");
 | 
			
		||||
    layout?.classList.add("toc");
 | 
			
		||||
    layout?.append(toc)
 | 
			
		||||
}
 | 
			
		||||
@@ -8,7 +8,7 @@
 | 
			
		||||
        "declaration": true,
 | 
			
		||||
        "strictNullChecks": true,
 | 
			
		||||
        "moduleResolution": "Node16",
 | 
			
		||||
        "target": "ES2020",
 | 
			
		||||
        "target": "ES2021",
 | 
			
		||||
        "rootDir": "src",
 | 
			
		||||
        "outDir": "lib/cjs",
 | 
			
		||||
        "module": "Node16",
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user