Modularize more and add swagger-ui

This commit is contained in:
Zack Rauen 2023-09-22 23:57:17 -04:00
parent 568ea271a4
commit 039a5ac2e3
26 changed files with 560 additions and 272 deletions

View File

@ -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
View File

@ -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",

View File

@ -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",

View File

@ -1,5 +1,8 @@
{
"extends": "../.eslintrc",
"env": {
"node": true
},
"rules": {
"no-console": "off"
}

View File

@ -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",

View File

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

View File

@ -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";
}
}
}

View File

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

View 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
View 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");
}
}

View 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");
}
}

View File

@ -1,5 +0,0 @@
export default function fixTableHeaders() {
Array.from(document.querySelectorAll("th")).forEach(el => {
if (!el.textContent?.trim()) el.classList.add("empty");
});
}

View File

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

View File

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

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

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

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

View File

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

View File

@ -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();
}
}
}
}

View File

@ -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)
}

View File

@ -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
View 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;
}

View File

@ -4,5 +4,5 @@
"rootDir": ".",
"noEmit": true
},
"include": ["src/**/*", "scripts/*", "static/*"],
"include": ["src/**/*", "scripts/*"],
}

View File

@ -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/**/*"]
}