mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-09-18 17:31:53 +08:00
Modularize more and add swagger-ui
This commit is contained in:
parent
568ea271a4
commit
039a5ac2e3
19
.eslintrc
19
.eslintrc
@ -2,25 +2,20 @@
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended-type-checked",
|
||||
"plugin:@typescript-eslint/stylistic-type-checked",
|
||||
"plugin:solid/typescript"
|
||||
"plugin:@typescript-eslint/stylistic-type-checked"
|
||||
],
|
||||
"env": {
|
||||
"node": true,
|
||||
"es2020": true,
|
||||
"browser": true,
|
||||
"jquery": true
|
||||
"browser": true
|
||||
},
|
||||
"plugins": ["@typescript-eslint", "solid"],
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"ignorePatterns": ["legacy/", "dist/"],
|
||||
"parserOptions": {
|
||||
"project": ["./tsconfig.eslint.json"],
|
||||
"tsconfigRootDir": ".",
|
||||
"ecmaVersion": 2022,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
}
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"accessor-pairs": "error",
|
||||
@ -92,9 +87,5 @@
|
||||
"wrap-iife": ["error", "inside"],
|
||||
"yield-star-spacing": "error",
|
||||
"yoda": "error"
|
||||
},
|
||||
"globals": {
|
||||
"api": "readonly",
|
||||
"NodeJS": "readonly"
|
||||
}
|
||||
}
|
7
package-lock.json
generated
7
package-lock.json
generated
@ -10,6 +10,7 @@
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@digitak/esrun": "^3.2.24",
|
||||
"@types/swagger-ui": "^3.52.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.7.2",
|
||||
"@typescript-eslint/parser": "^6.7.2",
|
||||
"dotenv": "^16.3.1",
|
||||
@ -935,6 +936,12 @@
|
||||
"integrity": "sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/swagger-ui": {
|
||||
"version": "3.52.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/swagger-ui/-/swagger-ui-3.52.0.tgz",
|
||||
"integrity": "sha512-SlufixEmh+8CLHNgTfAfCT1icNOF7bXboWabhHr1+hIolqlvfwYJGe7HgRcpI3ChE7HWASmEKLkMu34rxseJjQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "6.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.2.tgz",
|
||||
|
@ -7,12 +7,14 @@
|
||||
"build": "esrun scripts/build.ts",
|
||||
"build-main": "esrun scripts/build.ts -- --module=main",
|
||||
"build-styles": "esrun scripts/build.ts -- --module=styles",
|
||||
"dist": "esrun scripts/build.ts -- --minify",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@digitak/esrun": "^3.2.24",
|
||||
"@types/swagger-ui": "^3.52.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.7.2",
|
||||
"@typescript-eslint/parser": "^6.7.2",
|
||||
"dotenv": "^16.3.1",
|
||||
|
@ -1,5 +1,8 @@
|
||||
{
|
||||
"extends": "../.eslintrc",
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"rules": {
|
||||
"no-console": "off"
|
||||
}
|
||||
|
@ -73,9 +73,7 @@ async function runBuild() {
|
||||
bundle: true,
|
||||
outdir: path.join(rootDir, "dist"),
|
||||
format: "cjs",
|
||||
external: ["fs", "path", "electron", "@electron/remote"],
|
||||
banner: {js: "const require = mod => {try{return window.require(mod);}catch{return {};}};"},
|
||||
target: ["chrome96", "node16"],
|
||||
target: ["chrome96"],
|
||||
loader: {
|
||||
".png": "dataurl",
|
||||
".gif": "dataurl",
|
||||
|
@ -1,31 +0,0 @@
|
||||
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);
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
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";
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,24 @@
|
||||
/**
|
||||
* For some reason Trilium share chooses to have the
|
||||
* active link be just a <strong> rather than a true
|
||||
* link with a special style. This fixes that and
|
||||
* turns the <strong> back into an actual link
|
||||
* with the correct note id.
|
||||
*/
|
||||
export default function fixActiveLink() {
|
||||
const active = document.querySelector("#menu strong");
|
||||
if (!active) return; // Something is really wrong
|
||||
|
||||
// Currently active note id is stored on body
|
||||
const id = document.body.dataset.noteId;
|
||||
|
||||
// Create the new link
|
||||
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}`;
|
||||
|
||||
// Replace the <strong>
|
||||
active.replaceWith(link);
|
||||
}
|
19
src/main/fixes/externallinks.ts
Normal file
19
src/main/fixes/externallinks.ts
Normal file
@ -0,0 +1,19 @@
|
||||
/**
|
||||
* This function just lets us map some note links to be external links.
|
||||
* This was originally designed to link the Trilium GitHub via a note.
|
||||
*/
|
||||
export default function addExternalLinks(mapping: Record<string, string>) {
|
||||
for (const id in mapping) {
|
||||
const links = document.querySelectorAll<HTMLAnchorElement>(`a[href*="${id}"]`);
|
||||
if (!links.length) {
|
||||
// eslint-disable-next-line no-console
|
||||
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";
|
||||
}
|
||||
}
|
||||
}
|
62
src/main/fixes/submenu.ts
Normal file
62
src/main/fixes/submenu.ts
Normal file
@ -0,0 +1,62 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
} */
|
||||
|
||||
/**
|
||||
* General premise here is to find all submenus/sublists
|
||||
* and give them a submenu class. Then any list item
|
||||
* that contains one of these submenus gets a submenu-item
|
||||
* class. Additionally, any sublist that itself has a
|
||||
* sublist is given the class hasSubmenu.
|
||||
*/
|
||||
export default function fixSubMenu() {
|
||||
// Get every list item in the navigation
|
||||
const items = document.querySelectorAll("#menu ul li");
|
||||
for (const item of items) {
|
||||
const sublist = item.querySelector("ul");
|
||||
|
||||
// If the list item has no submenu, ignore and move on
|
||||
if (!sublist) continue;
|
||||
|
||||
// There seems to be some weird edge cases where a
|
||||
// sublist ul is added but has no list items and
|
||||
// in trilium the corresponding note has no children
|
||||
if (!sublist.children.length) {
|
||||
sublist.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the submenu is part of our blacklist, then
|
||||
// give it the hidden class. This is for pages
|
||||
// that have no subcategories and only a long
|
||||
// list of subpages.
|
||||
const ihtml = item.innerHTML;
|
||||
for (const bl of submenuBlacklist) {
|
||||
if (!ihtml.includes(bl)) continue;
|
||||
item.classList.add("hidden");
|
||||
}
|
||||
|
||||
// Finally, add the corresponding classes to the elements
|
||||
item.classList.add("submenu-item");
|
||||
sublist.classList.add("submenu");
|
||||
|
||||
// Currently, this is only used by the sidebar styles to
|
||||
// adjust margins. TODO: Might be worth investigating a
|
||||
// different method.
|
||||
if (sublist.querySelector("ul")?.children.length) sublist.classList.add("hasSubmenu");
|
||||
|
||||
}
|
||||
}
|
10
src/main/fixes/tableheaders.ts
Normal file
10
src/main/fixes/tableheaders.ts
Normal file
@ -0,0 +1,10 @@
|
||||
/**
|
||||
* This specifically fixes when you have empty corners
|
||||
* like on tables with column and row headers
|
||||
*/
|
||||
export default function fixTableHeaders() {
|
||||
const headers = document.querySelectorAll("th");
|
||||
for (const header of headers) {
|
||||
if (!header.textContent?.trim()) header.classList.add("empty");
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
export default function fixTableHeaders() {
|
||||
Array.from(document.querySelectorAll("th")).forEach(el => {
|
||||
if (!el.textContent?.trim()) el.classList.add("empty");
|
||||
});
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
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,22 +1,75 @@
|
||||
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";
|
||||
/* eslint-disable no-console */
|
||||
import fixActiveLink from "./fixes/activelink";
|
||||
import fixTableHeaders from "./fixes/tableheaders";
|
||||
import highlight from "./other/highlight";
|
||||
import buildSidenav from "./navigation/sidenav";
|
||||
import buildBreadcrumbs from "./navigation/breadcrumbs";
|
||||
import fixSubMenu from "./fixes/submenu";
|
||||
import generateTOC from "./navigation/toc";
|
||||
import addExternalLinks from "./fixes/externallinks";
|
||||
import injectSwagger from "./other/swagger";
|
||||
|
||||
// https://instance-name/api/notes/vW1cXaYNN7OM/download
|
||||
|
||||
const EXTERNAL_LINKS = {
|
||||
EGFtX8Uw96FQ: "https://github.com/zadam/trilium"
|
||||
};
|
||||
|
||||
try{fixActiveLink();} catch(e){console.error(e)}
|
||||
try{highlight();} catch(e){console.error(e)}
|
||||
try{fixTableHeaders();} catch(e){console.error(e)}
|
||||
// try{addLogo();} catch{}
|
||||
function $try<T extends (...a: unknown[]) => unknown>(func: T, ...args: Parameters<T>) {
|
||||
try {
|
||||
func.apply(func, args);
|
||||
}
|
||||
catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
try{fixSubMenu();} 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)}
|
||||
// const $try = (func, ...args) => {
|
||||
// try {
|
||||
// func.apply()
|
||||
// }
|
||||
// };
|
||||
|
||||
// Perform fixes first
|
||||
$try(fixActiveLink);
|
||||
$try(fixTableHeaders);
|
||||
$try(fixSubMenu);
|
||||
$try(addExternalLinks, EXTERNAL_LINKS);
|
||||
|
||||
// Now layout changes
|
||||
$try(buildBreadcrumbs);
|
||||
$try(buildSidenav);
|
||||
$try(generateTOC);
|
||||
|
||||
// Finally, other features
|
||||
$try(highlight);
|
||||
$try(injectSwagger);
|
||||
|
||||
|
||||
// try {fixActiveLink();}
|
||||
// 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 {buildSidenav();}
|
||||
// catch (e) {console.error(e);}
|
||||
|
||||
// try {buildBreadcrumbs();}
|
||||
// catch (e) {console.error(e);}
|
||||
|
||||
// try {generateTOC();}
|
||||
// catch (e) {console.error(e);}
|
||||
|
||||
// try {addExternalLinks(EXTERNAL_LINKS);}
|
||||
// catch (e) {console.error(e);}
|
||||
|
50
src/main/navigation/breadcrumbs.ts
Normal file
50
src/main/navigation/breadcrumbs.ts
Normal file
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* This build breadcrumb-style navigation using the existing
|
||||
* tree menu generated by Trilium. The concept is to find
|
||||
* the currently active link (fixed by an earlier script)
|
||||
* and follow that up to the root part of the menu
|
||||
*/
|
||||
export default function buildBreadcrumbsFromNav() {
|
||||
const container = document.createElement("ul");
|
||||
container.id = "breadcrumbs";
|
||||
|
||||
// Find currently active link
|
||||
const current = document.querySelector("#menu .active");
|
||||
if (!current) return; // Something went really wrong
|
||||
|
||||
// Clone the link and add it to the front of the breadcrumb list
|
||||
const firstItem = document.createElement("li");
|
||||
firstItem.append(current.cloneNode(true));
|
||||
container.prepend(firstItem);
|
||||
|
||||
// Walk the sublists upward until the root
|
||||
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
|
||||
|
||||
// Get the parent of the previous and add to front of breadcrumbs
|
||||
const ancestorItem = document.createElement("li");
|
||||
ancestorItem.append(clone);
|
||||
container.prepend(ancestorItem);
|
||||
|
||||
// Get the next sublist upward
|
||||
next = next?.parentElement?.closest("ul") ?? null;
|
||||
|
||||
// We are not yet at root, continue
|
||||
if (next) continue;
|
||||
|
||||
// Since next == null, we are at root, let's ue a pretty logo
|
||||
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;
|
||||
|
||||
// Add breadcrumb container to the main layout
|
||||
const main = document.getElementById("main");
|
||||
main?.prepend(container);
|
||||
}
|
30
src/main/navigation/sidenav.ts
Normal file
30
src/main/navigation/sidenav.ts
Normal file
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* This generates a docs-style sidebar navigation using the Trilium tree
|
||||
*/
|
||||
export default function addSideNav() {
|
||||
// Give all tier 2 list items category class... TODO: should this be done elsewhere?
|
||||
const categories = document.querySelectorAll("#menu > ul > li > ul > li");
|
||||
for (const cat of categories) cat.classList.add("category");
|
||||
|
||||
// Use the active link as our reference point
|
||||
const active = document.querySelector("#menu .active");
|
||||
|
||||
// From the active link, find the nearest category that is also a submenu item
|
||||
// If there is none, try to grab the nearest hidden submenu item (for non-
|
||||
// category style pages)
|
||||
const treeToClone = active?.closest(".category.submenu-item") ?? active?.closest(".submenu-item.hidden");
|
||||
if (!treeToClone) return; // If neither exist, 99% chance it's the homepage
|
||||
|
||||
// Clone the found node and add it to the sidebar
|
||||
const sidebar = document.createElement("ul");
|
||||
sidebar.id = "sidebar";
|
||||
const clone = treeToClone.cloneNode(true);
|
||||
sidebar.append(clone);
|
||||
|
||||
// If there's only a single item... probably not worth having a sidebar
|
||||
if (sidebar.querySelectorAll("li").length <= 1) return;
|
||||
|
||||
// Add the sidebar to the front of the layout container
|
||||
const layout = document.querySelector("#layout");
|
||||
layout?.prepend(sidebar);
|
||||
}
|
106
src/main/navigation/toc.ts
Normal file
106
src/main/navigation/toc.ts
Normal file
@ -0,0 +1,106 @@
|
||||
const slugify = (text: string) => text.toLowerCase().replace(/[^\w]/g, "-");
|
||||
|
||||
const getDepth = (el: Element) => parseInt(el.tagName.replace("H","").replace("h",""));
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate a ToC from all heading elements in the main content area.
|
||||
* This should go to full h6 depth and not be too opinionated. It
|
||||
* does assume a "sensible" structure in that you don't go from
|
||||
* h2 > h4 > h1 but rather h2 > h3 > h2 so you change by 1 and end
|
||||
* up at the same level as before.
|
||||
*/
|
||||
export default function generateTOC() {
|
||||
// Get all headings from the page and map them to already built elements
|
||||
const headings = Array.from(document.querySelectorAll("h1, h2, h3, h4, h5, h6"));
|
||||
if (headings.length <= 1) return; // But if there are none, let's do nothing
|
||||
const items = headings.map(h => buildItem(h));
|
||||
|
||||
// Setup the ToC list
|
||||
const toc = document.createElement("ul");
|
||||
toc.id = "toc";
|
||||
|
||||
// Get the depth of the first content heading on the page.
|
||||
// This depth will be used as reference for all other headings.
|
||||
// headings[0] === the <h1> from Trilium
|
||||
const firstDepth = getDepth(headings[1]);
|
||||
|
||||
// Loop over ALL headings including the first
|
||||
for (let h = 0; h < headings.length; h++) {
|
||||
// Get current heading and determine depth
|
||||
const current = headings[h];
|
||||
const currentDepth = getDepth(current);
|
||||
|
||||
// If it's the same depth as our first heading, add to ToC
|
||||
if (currentDepth === firstDepth) toc.append(items[h]);
|
||||
|
||||
// If this is the last element then it will have already
|
||||
// been added as a child or as same depth as first
|
||||
let nextIndex = h + 1;
|
||||
if (nextIndex >= headings.length) continue;
|
||||
|
||||
// Time to find all children of this heading
|
||||
const children = [];
|
||||
const childDepth = currentDepth + 1;
|
||||
let depthOfNext = getDepth(headings[nextIndex]);
|
||||
while (depthOfNext > currentDepth) {
|
||||
// If it's the expected depth, add as child
|
||||
if (depthOfNext === childDepth) children.push(nextIndex);
|
||||
nextIndex++;
|
||||
|
||||
// If the next index is valid, grab the depth for next loop
|
||||
// TODO: could this be done cleaner with a for loop?
|
||||
if (nextIndex < headings.length) depthOfNext = getDepth(headings[nextIndex]);
|
||||
else depthOfNext = currentDepth; // If the index was invalid, break loop
|
||||
}
|
||||
|
||||
// If this heading had children, add them as children
|
||||
if (children.length) {
|
||||
const ul = document.createElement("ul");
|
||||
for (const c of children) ul.append(items[c]);
|
||||
items[h].append(ul);
|
||||
}
|
||||
}
|
||||
|
||||
// Setup a moving "active" in the ToC that adjusts with the scroll state
|
||||
const sections = headings.slice(1);
|
||||
const links = toc.querySelectorAll("a");
|
||||
function changeLinkState() {
|
||||
let index = sections.length;
|
||||
|
||||
// Work backkwards to find the first matching section
|
||||
while (--index && window.scrollY + 50 < (sections[index] as HTMLElement).offsetTop) {} // eslint-disable-line no-empty
|
||||
|
||||
// Update the "active" item in ToC
|
||||
links.forEach((link) => link.classList.remove("active"));
|
||||
links[index].classList.add("active");
|
||||
}
|
||||
|
||||
// Initial render
|
||||
changeLinkState();
|
||||
window.addEventListener("scroll", changeLinkState);
|
||||
|
||||
// Finally, add the ToC to the end of layout. Give the layout a class for adjusting widths.
|
||||
const layout = document.querySelector("#layout");
|
||||
layout?.classList.add("toc");
|
||||
layout?.append(toc);
|
||||
}
|
53
src/main/other/highlight.ts
Normal file
53
src/main/other/highlight.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import {HLJSApi, HLJSPlugin} from "highlight.js";
|
||||
|
||||
|
||||
declare const hljs: HLJSApi;
|
||||
|
||||
|
||||
// Custom highlight.js plugin to highlight the `api` globals for Trilium
|
||||
const highlightTriliumApi: HLJSPlugin = {
|
||||
"after:highlight": (result) => {
|
||||
result.value = result.value.replaceAll(/([^A-Za-z0-9])api\./g, function(match, prefix) {
|
||||
return `${prefix}<span class="hljs-variable language_">api</span>.`;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Custom highlight.js plugin to highlight JQuery function usage
|
||||
const highlightJQuery: HLJSPlugin = {
|
||||
"after:highlight": (result) => {
|
||||
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>`;
|
||||
});
|
||||
// TODO: add highlighting for static calls like $.ajax
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Let's highlight some codeblocks!
|
||||
* TODO: check if there are codeblocks on the page before performing this action
|
||||
*/
|
||||
export default function addHljs() {
|
||||
// Add the hightlight.js styles from the child note of this script
|
||||
// TODO: make this a mapping
|
||||
const link = document.createElement("link");
|
||||
link.rel = "stylesheet";
|
||||
link.href = "api/notes/cVaK9ZJwx5Hs/download";
|
||||
document.head.append(link);
|
||||
|
||||
// Add the highlight.js script too
|
||||
// TODO: make this a mappin as well
|
||||
const script = document.createElement("script");
|
||||
script.src = "api/notes/6PVElIem02b5/download";
|
||||
script.addEventListener("load", () => {
|
||||
// This registers the JS Frontend and JS Backend types as javascript aliases for highlighting purposes
|
||||
hljs.registerAliases(["application-javascript-env-frontend", "application-javascript-env-backend"], {languageName: "javascript"});
|
||||
|
||||
// Add our custom plugins and highlight all on page
|
||||
hljs.addPlugin(highlightTriliumApi);
|
||||
hljs.addPlugin(highlightJQuery);
|
||||
hljs.highlightAll();
|
||||
});
|
||||
document.head.append(script);
|
||||
}
|
35
src/main/other/swagger.ts
Normal file
35
src/main/other/swagger.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import SwaggerUI, {SwaggerUIOptions} from "swagger-ui";
|
||||
|
||||
|
||||
declare const SwaggerUIBundle: typeof SwaggerUI;
|
||||
const opts: SwaggerUIOptions = {
|
||||
url: "https://raw.githubusercontent.com/zadam/trilium/master/src/etapi/etapi.openapi.yaml"
|
||||
};
|
||||
|
||||
/**
|
||||
* Add swagger to the ETAPI page!
|
||||
*/
|
||||
export default function injectSwagger() {
|
||||
// Check if it's the ETAPI page, otherwise avoid dependency
|
||||
const noteId = document.body.dataset.noteId;
|
||||
if (noteId !== "pPIXi0uwF5GX") return; // TODO: make this a param
|
||||
const main = document.getElementById("main")!;
|
||||
main.innerHTML = "";
|
||||
opts.domNode = main;
|
||||
// Add the swagger-ui styles from unpkg
|
||||
// TODO: make this hosted by trilium
|
||||
const link = document.createElement("link");
|
||||
link.rel = "stylesheet";
|
||||
link.href = "https://unpkg.com/swagger-ui-dist@4.5.0/swagger-ui.css";
|
||||
document.head.append(link);
|
||||
|
||||
// Add the swagger-ui script too
|
||||
// TODO: make this hosted by trilium
|
||||
const script = document.createElement("script");
|
||||
script.src = "https://unpkg.com/swagger-ui-dist@4.5.0/swagger-ui-bundle.js";
|
||||
script.addEventListener("load", () => {
|
||||
// This immediately generated the swagger-ui in the main element
|
||||
SwaggerUIBundle(opts); // eslint-disable-line new-cap
|
||||
});
|
||||
document.head.append(script);
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
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);
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
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)
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
@import "./swagger.css";
|
||||
|
||||
* {box-sizing: border-box;}
|
||||
|
||||
:root {
|
||||
@ -8,6 +10,7 @@
|
||||
--ck-color-table-caption-text: gray;
|
||||
--bg-primary: #1E1E1E;
|
||||
--bg-secondary: #3C3C3C;
|
||||
--text-muted: #777;
|
||||
--text-primary: #ddd;
|
||||
--text-heading: #fff;
|
||||
|
||||
@ -20,6 +23,7 @@
|
||||
--container-width: 1400px /*calc(100% - 10%)*/;
|
||||
--pane-size: 20%;
|
||||
scroll-padding-top: calc(72px + 1rem);
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
body {
|
||||
|
85
src/styles/swagger.css
Normal file
85
src/styles/swagger.css
Normal file
@ -0,0 +1,85 @@
|
||||
#main .swagger-ui .scheme-container {
|
||||
background: #20212B;
|
||||
}
|
||||
|
||||
#main .swagger-ui .opblock .opblock-section-header {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
#main .swagger-ui .opblock-body pre.microlight {
|
||||
background: #20212B!important;
|
||||
}
|
||||
|
||||
#main .swagger-ui,
|
||||
#main .swagger-ui .info li,
|
||||
#main .swagger-ui .info p,
|
||||
#main .swagger-ui .info table,
|
||||
#main .swagger-ui .opblock-description-wrapper p,
|
||||
#main .swagger-ui .opblock-external-docs-wrapper p,
|
||||
#main .swagger-ui .opblock-title_normal p,
|
||||
#main .swagger-ui .response-col_status,
|
||||
#main .swagger-ui .response-col_links {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
#main .swagger-ui .btn,
|
||||
#main .swagger-ui .model,
|
||||
#main .swagger-ui .tab li,
|
||||
#main .swagger-ui table thead tr td,
|
||||
#main .swagger-ui table thead tr th,
|
||||
#main .swagger-ui .parameter__type,
|
||||
#main .swagger-ui .parameter__name,
|
||||
#main .swagger-ui thead tr td.col_header,
|
||||
#main .swagger-ui .model-title {
|
||||
color: var(--text-heading);
|
||||
}
|
||||
|
||||
#main .swagger-ui .model .property.primitive {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
#main .swagger-ui .prop-type {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
#main .swagger-ui svg {
|
||||
fill: var(--text-heading);
|
||||
}
|
||||
|
||||
|
||||
#main .swagger-ui .model-toggle::after {
|
||||
filter: invert(100%);
|
||||
}
|
||||
|
||||
#main .swagger-ui .btn {
|
||||
border: 2px solid var(--text-heading);
|
||||
transition: transform 200ms ease;
|
||||
}
|
||||
|
||||
#main .swagger-ui .btn:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
#main .swagger-ui input[disabled],
|
||||
#main .swagger-ui select[disabled],
|
||||
#main .swagger-ui textarea[disabled],
|
||||
#main .swagger-ui select {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
color: var(--text-primary);
|
||||
border: 2px solid rgba(0,0,0,0.3);
|
||||
appearance: auto;
|
||||
}
|
||||
|
||||
|
||||
#main .models > h4 {
|
||||
margin: 5px 10px;
|
||||
}
|
||||
|
||||
|
||||
#main .swagger-ui .opblock-summary-control {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
#main .swagger-ui .authorization__btn {
|
||||
padding-left: 0;
|
||||
}
|
@ -4,5 +4,5 @@
|
||||
"rootDir": ".",
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["src/**/*", "scripts/*", "static/*"],
|
||||
"include": ["src/**/*", "scripts/*"],
|
||||
}
|
@ -5,30 +5,12 @@
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"resolveJsonModule": true,
|
||||
"declaration": true,
|
||||
"strictNullChecks": true,
|
||||
"moduleResolution": "Node16",
|
||||
"target": "ES2021",
|
||||
"rootDir": "src",
|
||||
"outDir": "lib/cjs",
|
||||
"module": "Node16",
|
||||
"jsx": "preserve",
|
||||
"jsxImportSource": "solid-js",
|
||||
"module": "Node16"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"typedocOptions": {
|
||||
"name": "TriliumETAPI",
|
||||
"entryPoints": ["src/index.ts"],
|
||||
"out": "docs",
|
||||
"includeVersion": true,
|
||||
"navigationLinks": {
|
||||
"Trilium": "https://github.com/zadam/trilium"
|
||||
},
|
||||
"sidebarLinks": {},
|
||||
"navigation": {
|
||||
"fullTree": true,
|
||||
"includeCategories": true,
|
||||
"includeGroups": false
|
||||
}
|
||||
}
|
||||
"include": ["src/**/*"]
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user