Compare commits

...

9 Commits

Author SHA1 Message Date
Elian Doran
0c399a676a chore(share): fix typecheck issue 2025-10-24 23:28:42 +03:00
Elian Doran
395f33cd5b chore(share): bring back boxicons 2025-10-24 22:32:42 +03:00
Elian Doran
21b20cf575 chore(share): bring back most of the logic 2025-10-24 21:18:06 +03:00
Elian Doran
e3dd25b591 chore(share): set up math 2025-10-24 21:13:40 +03:00
Elian Doran
b9a4e7ab11 chore(share): enable code splitting 2025-10-24 20:52:54 +03:00
Elian Doran
6ae67c410c chore(share): load Mermaid only when necessary 2025-10-24 20:52:47 +03:00
Elian Doran
4ef7667484 chore(share): bring back inline mermaid rendering 2025-10-24 19:09:19 +03:00
Elian Doran
3660e2f127 refactor(share): store assets at /share/asset level 2025-10-24 19:00:26 +03:00
Elian Doran
357d294f2d chore(export/share): address review 2025-10-24 18:25:16 +03:00
14 changed files with 117 additions and 75 deletions

View File

@@ -9,16 +9,6 @@ async function ensureJQuery() {
(window as any).$ = $;
}
async function applyMath() {
const anyMathBlock = document.querySelector("#content .math-tex");
if (!anyMathBlock) {
return;
}
const renderMathInElement = (await import("./services/math.js")).renderMathInElement;
renderMathInElement(document.getElementById("content"));
}
async function formatCodeBlocks() {
const anyCodeBlock = document.querySelector("#content pre");
if (!anyCodeBlock) {
@@ -31,54 +21,4 @@ async function formatCodeBlocks() {
async function setupTextNote() {
formatCodeBlocks();
applyMath();
const setupMermaid = (await import("./share/mermaid.js")).default;
setupMermaid();
}
/**
* Fetch note with given ID from backend
*
* @param noteId of the given note to be fetched. If false, fetches current note.
*/
async function fetchNote(noteId: string | null = null) {
if (!noteId) {
noteId = document.body.getAttribute("data-note-id");
}
const resp = await fetch(`api/notes/${noteId}`);
return await resp.json();
}
document.addEventListener(
"DOMContentLoaded",
() => {
const noteType = determineNoteType();
if (noteType === "text") {
setupTextNote();
}
const toggleMenuButton = document.getElementById("toggleMenuButton");
const layout = document.getElementById("layout");
if (toggleMenuButton && layout) {
toggleMenuButton.addEventListener("click", () => layout.classList.toggle("showMenu"));
}
},
false
);
function determineNoteType() {
const bodyClass = document.body.className;
const match = bodyClass.match(/type-([^\s]+)/);
return match ? match[1] : null;
}
// workaround to prevent webpack from removing "fetchNote" as dead code:
// add fetchNote as property to the window object
Object.defineProperty(window, "fetchNote", {
value: fetchNote
});

View File

@@ -7,6 +7,7 @@ async function main() {
// Copy assets
build.copy("src/assets", "assets/");
build.triggerBuildAndCopyTo("packages/share-theme", "share-theme/assets/");
build.copy("/packages/share-theme/src/templates", "share-theme/templates/");
// Copy node modules dependencies

View File

@@ -32,6 +32,7 @@ async function register(app: express.Application) {
req.url = `/${assetUrlFragment}` + req.url;
vite.middlewares(req, res, next);
});
app.use(`/share/assets/`, express.static(path.join(srcRoot, "../../packages/share-theme/dist")));
} else {
const publicDir = path.join(resourceDir, "public");
if (!existsSync(publicDir)) {
@@ -42,6 +43,7 @@ async function register(app: express.Application) {
app.use(`/${assetUrlFragment}/stylesheets`, persistentCacheStatic(path.join(publicDir, "stylesheets")));
app.use(`/${assetUrlFragment}/fonts`, persistentCacheStatic(path.join(publicDir, "fonts")));
app.use(`/${assetUrlFragment}/translations/`, persistentCacheStatic(path.join(publicDir, "translations")));
app.use(`/share/assets/`, persistentCacheStatic(path.join(resourceDir, "share-theme/assets")));
app.use(`/node_modules/`, persistentCacheStatic(path.join(publicDir, "node_modules")));
}
app.use(`/${assetUrlFragment}/images`, persistentCacheStatic(path.join(resourceDir, "assets", "images")));

View File

@@ -27,14 +27,14 @@ import { NoteType } from "@triliumnext/commons";
async function exportToZip(taskContext: TaskContext<"export">, branch: BBranch, format: ExportFormat, res: Response | fs.WriteStream, setHeaders = true, zipExportOptions?: AdvancedExportOptions) {
if (!["html", "markdown", "share"].includes(format)) {
throw new ValidationError(`Only 'html' and 'markdown' allowed as export format, '${format}' given`);
throw new ValidationError(`Only 'html', 'markdown' and 'share' allowed as export format, '${format}' given`);
}
const archive = archiver("zip", {
zlib: { level: 9 } // Sets the compression level.
});
const rewriteFn = (zipExportOptions?.customRewriteLinks ? zipExportOptions?.customRewriteLinks(rewriteLinks, getNoteTargetUrl) : rewriteLinks);
const provider= buildProvider();
const provider = buildProvider();
const noteIdToMeta: Record<string, NoteMeta> = {};

View File

@@ -9,15 +9,13 @@ import type BBranch from "../becca/entities/bbranch.js";
import { t } from "i18next";
import SBranch from "./shaca/entities/sbranch.js";
import options from "../services/options.js";
import utils, { getResourceDir, isDev, safeExtractMessageAndStackFromError } from "../services/utils.js";
import app_path from "../services/app_path.js";
import utils, { isDev, safeExtractMessageAndStackFromError } from "../services/utils.js";
import ejs from "ejs";
import log from "../services/log.js";
import { join } from "path";
import { readFileSync } from "fs";
const shareAdjustedAssetPath = isDev ? assetPath : `../${assetPath}`;
const shareAdjustedAppPath = isDev ? app_path : `../${app_path}`;
const templateCache: Map<string, string> = new Map();
/**
@@ -99,9 +97,9 @@ export function renderNoteContent(note: SNote) {
// Determine CSS to load.
const cssToLoad: string[] = [];
if (!isDev && !note.isLabelTruthy("shareOmitDefaultCss")) {
cssToLoad.push(`${shareAdjustedAssetPath}/src/share.css`);
cssToLoad.push(`${shareAdjustedAssetPath}/src/boxicons.css`);
if (!note.isLabelTruthy("shareOmitDefaultCss")) {
cssToLoad.push(`assets/styles.css`);
cssToLoad.push(`assets/scripts.css`);
}
for (const cssRelation of note.getRelations("shareCss")) {
cssToLoad.push(`api/notes/${cssRelation.value}/download`);
@@ -109,7 +107,7 @@ export function renderNoteContent(note: SNote) {
// Determine JS to load.
const jsToLoad: string[] = [
`${shareAdjustedAppPath}/share.js`
"assets/scripts.js"
];
for (const jsRelation of note.getRelations("shareJs")) {
jsToLoad.push(`api/notes/${jsRelation.value}/download`);
@@ -147,7 +145,6 @@ function renderNoteContentInternal(note: SNote | BNote, renderArgs: RenderArgs)
isEmpty,
assetPath: shareAdjustedAssetPath,
assetUrlFragment,
appPath: shareAdjustedAppPath,
showLoginInShareTheme,
t,
isDev,

View File

@@ -21,6 +21,11 @@
"Zerebos <me@zerebos.com>"
],
"license": "Apache-2.0",
"dependencies": {
"katex": "0.16.25",
"mermaid": "11.12.0",
"boxicons": "2.1.4"
},
"devDependencies": {
"@digitak/esrun": "3.2.26",
"@types/swagger-ui": "5.21.1",

View File

@@ -1,4 +1,3 @@
import fs from "node:fs";
import path from "node:path";
// import {fileURLToPath} from "node:url";
@@ -51,8 +50,9 @@ async function runBuild() {
await esbuild.build({
entryPoints: entryPoints,
bundle: true,
splitting: true,
outdir: path.join(rootDir, "dist"),
format: "cjs",
format: "esm",
target: ["chrome96"],
loader: {
".png": "dataurl",
@@ -60,6 +60,8 @@ async function runBuild() {
".woff": "dataurl",
".woff2": "dataurl",
".ttf": "dataurl",
".eot": "empty",
".svg": "empty",
".html": "text",
".css": "css"
},

View File

@@ -3,6 +3,10 @@ import setupExpanders from "./modules/expanders";
import setupMobileMenu from "./modules/mobile";
import setupSearch from "./modules/search";
import setupThemeSelector from "./modules/theme";
import setupMermaid from "./modules/mermaid";
import setupMath from "./modules/math";
import api from "./modules/api";
import "boxicons/css/boxicons.min.css";
function $try<T extends (...a: unknown[]) => unknown>(func: T, ...args: Parameters<T>) {
try {
@@ -13,8 +17,39 @@ function $try<T extends (...a: unknown[]) => unknown>(func: T, ...args: Paramete
}
}
Object.assign(window, api);
$try(setupThemeSelector);
$try(setupToC);
$try(setupExpanders);
$try(setupMobileMenu);
$try(setupSearch);
function setupTextNote() {
$try(setupMermaid);
$try(setupMath);
}
document.addEventListener(
"DOMContentLoaded",
() => {
const noteType = determineNoteType();
if (noteType === "text") {
setupTextNote();
}
const toggleMenuButton = document.getElementById("toggleMenuButton");
const layout = document.getElementById("layout");
if (toggleMenuButton && layout) {
toggleMenuButton.addEventListener("click", () => layout.classList.toggle("showMenu"));
}
},
false
);
function determineNoteType() {
const bodyClass = document.body.className;
const match = bodyClass.match(/type-([^\s]+)/);
return match ? match[1] : null;
}

View File

@@ -0,0 +1,18 @@
/**
* Fetch note with given ID from backend
*
* @param noteId of the given note to be fetched. If false, fetches current note.
*/
async function fetchNote(noteId: string | null = null) {
if (!noteId) {
noteId = document.body.getAttribute("data-note-id");
}
const resp = await fetch(`api/notes/${noteId}`);
return await resp.json();
}
export default {
fetchNote
};

View File

@@ -0,0 +1,14 @@
import "katex/dist/katex.min.css";
export default async function setupMath() {
const anyMathBlock = document.querySelector("#content .math-tex");
if (!anyMathBlock) {
return;
}
const renderMathInElement = (await import("katex/contrib/auto-render")).default;
await import("katex/contrib/mhchem");
renderMathInElement(document.getElementById("content"));
document.body.classList.add("math-loaded");
}

View File

@@ -1,7 +1,12 @@
import mermaid from "mermaid";
export default async function setupMermaid() {
const mermaidEls = document.querySelectorAll("#content pre code.language-mermaid");
if (mermaidEls.length === 0) {
return;
}
export default function setupMermaid() {
for (const codeBlock of document.querySelectorAll("#content pre code.language-mermaid")) {
const mermaid = (await import("mermaid")).default;
for (const codeBlock of mermaidEls) {
const parentPre = codeBlock.parentElement;
if (!parentPre) {
continue;

View File

@@ -46,4 +46,8 @@
#content img {
max-width: 100%;
}
body:not(.math-loaded) .math-tex {
visibility: hidden;
}

5
packages/share-theme/src/types.d.ts vendored Normal file
View File

@@ -0,0 +1,5 @@
declare module "katex/contrib/auto-render" {
export default function renderMathInElement(elem: HTMLElement, options?: {})
}
declare module "katex/contrib/mhchem" {}

14
pnpm-lock.yaml generated
View File

@@ -1328,6 +1328,16 @@ importers:
version: 1.2.0
packages/share-theme:
dependencies:
boxicons:
specifier: 2.1.4
version: 2.1.4
katex:
specifier: 0.16.25
version: 0.16.25
mermaid:
specifier: 11.12.0
version: 11.12.0
devDependencies:
'@digitak/esrun':
specifier: 3.2.26
@@ -15335,6 +15345,8 @@ snapshots:
'@ckeditor/ckeditor5-utils': 47.1.0
ckeditor5: 47.1.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
es-toolkit: 1.39.5
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-editor-multi-root@47.1.0':
dependencies:
@@ -15831,6 +15843,8 @@ snapshots:
'@ckeditor/ckeditor5-ui': 47.1.0
'@ckeditor/ckeditor5-utils': 47.1.0
ckeditor5: 47.1.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-restricted-editing@47.1.0':
dependencies: