fix: obsidian bot screaming

This commit is contained in:
Walid 2026-06-07 18:11:06 +06:00
parent 156677f32d
commit b338b9074c
7 changed files with 373 additions and 297 deletions

View file

@ -13,10 +13,6 @@ const external = [
"@codemirror/language",
"@codemirror/search",
"@codemirror/autocomplete",
"@codemirror/highlight",
"@codemirror/history",
"@codemirror/fold",
"@codemirror/stream-parser",
"@lezer/common",
"@lezer/lr",
...builtinModules,
@ -28,7 +24,7 @@ const ctx = await esbuild.context({
outfile: "main.js",
external,
format: "cjs",
target: "es2018",
target: "es2022",
platform: "browser",
sourcemap: isProduction ? false : "inline",
minify: isProduction,

82
main.js

File diff suppressed because one or more lines are too long

238
main.ts
View file

@ -316,16 +316,16 @@ function splitMarkdownSections(md: string): string[] {
.filter(Boolean);
}
async function renderMarkdownToHtml(
async function renderMarkdownToEl(
app: App,
markdown: string,
sourcePath: string,
component: Component,
): Promise<string> {
const temp = document.createElement("div");
): Promise<HTMLElement> {
const temp = activeDocument.createElement("div");
await MarkdownRenderer.render(app, markdown, temp, sourcePath, component);
postProcessRenderedHTML(temp);
return temp.innerHTML;
return temp;
}
function slugifyHeading(text: string): string {
@ -583,7 +583,7 @@ const UNSPLITTABLE_TAGS = new Set([
const HEIGHT_EPS = 2; // px
function measureNodesHeight(nodes: HTMLElement[], measureEl: HTMLElement): number {
measureEl.innerHTML = "";
measureEl.empty();
for (const node of nodes) measureEl.appendChild(node.cloneNode(true));
return measureEl.getBoundingClientRect().height;
}
@ -600,7 +600,7 @@ function makeFitFn(
// ── Inline (text) splitter ───────────────────────────────────────────────────
function trimLeadingWhitespace(el: HTMLElement): void {
const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
const walker = activeDocument.createTreeWalker(el, NodeFilter.SHOW_TEXT);
while (walker.nextNode()) {
const node = walker.currentNode as Text;
const trimmed = (node.textContent ?? "").replace(/^\s+/, "");
@ -613,7 +613,7 @@ function buildInlineSplitAt(
el: HTMLElement,
splitOffset: number,
): [HTMLElement, HTMLElement] | null {
const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
const walker = activeDocument.createTreeWalker(el, NodeFilter.SHOW_TEXT);
let count = 0;
let target: Text | null = null;
let localOffset = 0;
@ -631,11 +631,11 @@ function buildInlineSplitAt(
if (!target) return null;
const range1 = document.createRange();
const range1 = activeDocument.createRange();
range1.selectNodeContents(el);
range1.setEnd(target, localOffset);
const range2 = document.createRange();
const range2 = activeDocument.createRange();
range2.selectNodeContents(el);
range2.setStart(target, localOffset);
@ -750,7 +750,7 @@ function buildTableWithRows(tableEl: HTMLTableElement, rows: HTMLTableRowElement
const colgroup = tableEl.querySelector("colgroup");
if (colgroup) clone.appendChild(colgroup.cloneNode(true));
if (tableEl.tHead) clone.appendChild(tableEl.tHead.cloneNode(true));
const tbody = document.createElement("tbody");
const tbody = activeDocument.createElement("tbody");
for (const row of rows) tbody.appendChild(row.cloneNode(true));
clone.appendChild(tbody);
return clone;
@ -856,40 +856,42 @@ function splitElement(
// ── Main pagination loop ─────────────────────────────────────────────────────
function paginateHTML(
html: string,
function paginateEl(
sourceEl: HTMLElement,
contentWidthPx: number,
contentHeightPx: number,
docCSS: string,
): HTMLElement[][] {
// Fixed-position sandbox: out of any scroll container, never affects layout.
const sandbox = document.createElement("div");
sandbox.style.cssText = [
// Fixed-position sandbox inside a shadow root so the measurement CSS is
// fully scoped and never pollutes the main document. Using adoptedStyleSheets
// avoids creating a <style> element.
const sandboxHost = activeDocument.createElement("div");
sandboxHost.style.cssText = [
"position:fixed", "top:0", "left:-99999px",
`width:${contentWidthPx}px`,
"visibility:hidden", "pointer-events:none", "z-index:-1",
].join(";");
// Inject doc CSS via adoptedStyleSheets to avoid creating a <style> element.
const docSheet = new CSSStyleSheet();
docSheet.replaceSync(docCSS);
const prevSheets = [...document.adoptedStyleSheets];
document.adoptedStyleSheets = [...prevSheets, docSheet];
const sandboxShadow = sandboxHost.attachShadow({ mode: "open" });
const inner = document.createElement("div");
const sandboxSheet = new CSSStyleSheet();
sandboxSheet.replaceSync(docCSS);
sandboxShadow.adoptedStyleSheets = [sandboxSheet];
const inner = activeDocument.createElement("div");
inner.className = "mpdf-doc";
// Use DOMParser to safely parse the rendered HTML instead of innerHTML.
const parsed = new DOMParser().parseFromString(html, "text/html");
Array.from(parsed.body.childNodes).forEach((n) => inner.appendChild(document.importNode(n, true)));
sandbox.appendChild(inner);
for (const child of Array.from(sourceEl.children)) {
inner.appendChild(child.cloneNode(true));
}
sandboxShadow.appendChild(inner);
// Measurement div: same width, always empty before each measurement.
const measure = document.createElement("div");
const measure = activeDocument.createElement("div");
measure.className = "mpdf-doc";
measure.style.cssText = `position:absolute;top:0;left:0;width:${contentWidthPx}px;visibility:hidden;`;
sandbox.appendChild(measure);
sandboxShadow.appendChild(measure);
document.body.appendChild(sandbox);
activeDocument.body.appendChild(sandboxHost);
const pages: HTMLElement[][] = [];
try {
@ -942,8 +944,7 @@ function paginateHTML(
if (currentPage.length > 0) pages.push(currentPage);
} finally {
document.body.removeChild(sandbox);
document.adoptedStyleSheets = prevSheets;
activeDocument.body.removeChild(sandboxHost);
}
return pages.length > 0 ? pages : [[]];
}
@ -1001,13 +1002,13 @@ function buildPageLayouts(allPages: HTMLElement[][], s: PDFExportSettings): Page
// ─── Plugin ───────────────────────────────────────────────────────────────────
export default class MarkdownPDFPlugin extends Plugin {
settings: PDFExportSettings;
declare settings: PDFExportSettings;
activeModal: PDFExportModal | null = null;
async onload() {
await this.loadSettings();
this.addCommand({
id: "open-advanced-pdf-export",
id: "open-panel",
name: "Open Panel",
callback: () => this.openModal(),
});
@ -1033,7 +1034,7 @@ export default class MarkdownPDFPlugin extends Plugin {
}
async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData() as Partial<PDFExportSettings>);
}
async saveSettings() {
@ -1221,12 +1222,11 @@ class PDFExportModal extends Modal {
zoomSlider.step = "0.05";
zoomSlider.value = String(s.previewScale);
zoomSlider.addClass("mpdf-zoom-slider");
zoomSlider.addEventListener("input", async () => {
zoomSlider.addEventListener("input", () => {
const v = parseFloat(zoomSlider.value);
this.plugin.settings.previewScale = v;
zoomLabel.textContent = Math.round(v * 100) + "%";
await this.plugin.saveSettings();
this.renderPreviewOnly();
void this.plugin.saveSettings().then(() => { this.renderPreviewOnly(); });
});
const breakBtn = left.createEl("button", { cls: "mpdf-btn", text: "Insert Page Break" });
@ -1366,15 +1366,15 @@ class PDFExportModal extends Modal {
const docCSS = buildDocCSS(s, isRTL);
const sourcePath = this.currentFile?.path ?? "pdf-export";
const sectionHtml = await Promise.all(
sections.map((sec) => renderMarkdownToHtml(this.app, sec.trim(), sourcePath, this.renderComponent)),
const sectionEls = await Promise.all(
sections.map((sec) => renderMarkdownToEl(this.app, sec.trim(), sourcePath, this.renderComponent)),
);
if (token !== this.renderToken) return;
const allPages: HTMLElement[][] = [];
for (const html of sectionHtml) {
allPages.push(...paginateHTML(html, contentW, contentH, docCSS));
for (const sectionEl of sectionEls) {
allPages.push(...paginateEl(sectionEl, contentW, contentH, docCSS));
}
const layouts = buildPageLayouts(allPages, s);
@ -1392,13 +1392,13 @@ class PDFExportModal extends Modal {
}
private showLoading() {
this.loadingOverlayEl.addClass("mpdf-loading-overlay--visible");
this.loadingOverlayEl.addClass("is-active");
this.renderBtn.disabled = true;
this.renderBtn.textContent = "Rendering…";
}
private hideLoading() {
this.loadingOverlayEl.removeClass("mpdf-loading-overlay--visible");
this.loadingOverlayEl.removeClass("is-active");
this.renderBtn.disabled = false;
this.renderBtn.textContent = "⟳ Render PDF";
}
@ -1427,28 +1427,25 @@ class PDFExportModal extends Modal {
const scaledH = Math.round(ph * scale);
const wrap = this.previewEl.createEl("div", { cls: "mpdf-page-wrap" });
wrap.style.cssText = `width:${scaledW}px;height:${scaledH}px;position:relative;flex-shrink:0;`;
wrap.style.cssText = `width:${scaledW}px;height:${scaledH}px;`;
wrap.createEl("div", { cls: "mpdf-page-label", text: `Page ${layout.pageNum} of ${layout.totalPages}` });
pageWraps.push(wrap);
const scaleWrap = wrap.createEl("div", { cls: "mpdf-page-scale" });
scaleWrap.style.cssText = `width:${scaledW}px;height:${scaledH}px;overflow:hidden;position:relative;`;
scaleWrap.style.cssText = `width:${scaledW}px;height:${scaledH}px;`;
// Each page renders inside a shadow root so Obsidian's theme CSS cannot
// reach the page content across the shadow boundary.
const shadowHost = document.createElement("div");
shadowHost.style.cssText = [
`width:${pw}px`, `height:${ph}px`,
`transform:scale(${scale})`, "transform-origin:top left",
"position:absolute", "top:0", "left:0",
].join(";");
const shadowHost = activeDocument.createElement("div");
shadowHost.addClass("mpdf-shadow-host");
shadowHost.style.cssText = `width:${pw}px;height:${ph}px;transform:scale(${scale});`;
scaleWrap.appendChild(shadowHost);
const shadow = shadowHost.attachShadow({ mode: "open" });
// Inject shadow CSS via adoptedStyleSheets to avoid creating a <style> element.
// Classes mpdf-hf-center / mpdf-hf-end are used by header and footer spans.
const shadowCSSText = `
// Use adoptedStyleSheets instead of a <style> element — avoids the
// no-create-style-element lint rule and keeps shadow CSS fully scoped.
const shadowCSS = `
:host {
display: block;
width: ${pw}px;
@ -1461,33 +1458,33 @@ class PDFExportModal extends Modal {
}
*, *::before, *::after { box-sizing: border-box; }
.mpdf-hf-center { flex: 1; text-align: center; }
.mpdf-hf-end { margin-left: auto; }
.mpdf-hf-right { margin-left: auto; }
${docCSS}
`;
const shadowSheet = new CSSStyleSheet();
shadowSheet.replaceSync(shadowCSSText);
shadow.adoptedStyleSheets = [shadowSheet];
const pageSheet = new CSSStyleSheet();
pageSheet.replaceSync(shadowCSS);
shadow.adoptedStyleSheets = [pageSheet];
// ── Header ───────────────────────────────────────────────────────────────
const hasHeader = s.showHeader && (layout.headerLeft || layout.headerCenter || layout.headerRight);
if (hasHeader) {
const hdr = document.createElement("div");
const hdr = activeDocument.createElement("div");
hdr.style.cssText = [
"position:absolute", `top:${mTop * 0.4}px`, `left:${mLeft}px`, `right:${mLeft}px`,
"display:flex", "align-items:center",
"font-size:9px", "color:#999", `font-family:${fontFamily}`, "white-space:nowrap",
].join(";");
if (layout.headerCenter) {
const span = document.createElement("span");
const span = activeDocument.createElement("span");
span.className = "mpdf-hf-center";
span.textContent = layout.headerCenter;
hdr.appendChild(span);
} else {
const leftSpan = document.createElement("span");
const leftSpan = activeDocument.createElement("span");
leftSpan.textContent = layout.headerLeft;
hdr.appendChild(leftSpan);
const rightSpan = document.createElement("span");
rightSpan.className = "mpdf-hf-end";
const rightSpan = activeDocument.createElement("span");
rightSpan.className = "mpdf-hf-right";
rightSpan.textContent = layout.headerRight;
hdr.appendChild(rightSpan);
}
@ -1495,7 +1492,7 @@ class PDFExportModal extends Modal {
}
// ── Content ──────────────────────────────────────────────────────────────
const contentDiv = document.createElement("div");
const contentDiv = activeDocument.createElement("div");
contentDiv.className = "mpdf-doc";
if (isRTL) contentDiv.setAttribute("dir", "rtl");
// No height/overflow:hidden — the :host clips at the page boundary.
@ -1526,7 +1523,7 @@ class PDFExportModal extends Modal {
// Skip empty footer divs (e.g. page 1 when showHeaderFooterOnFirstPage is off).
const hasFooter = s.showFooter && (layout.footerLeft || layout.footerRight || layout.footerCenter);
if (hasFooter) {
const footer = document.createElement("div");
const footer = activeDocument.createElement("div");
footer.style.cssText = [
"position:absolute", "bottom:0", "left:0", "right:0",
`height:${footerH}px`, "display:flex", "align-items:center",
@ -1535,16 +1532,16 @@ class PDFExportModal extends Modal {
].filter(Boolean).join(";");
if (layout.footerCenter) {
const span = document.createElement("span");
const span = activeDocument.createElement("span");
span.className = "mpdf-hf-center";
span.textContent = layout.footerCenter;
footer.appendChild(span);
} else {
const left = document.createElement("span");
const left = activeDocument.createElement("span");
left.textContent = layout.footerLeft;
footer.appendChild(left);
const right = document.createElement("span");
right.className = "mpdf-hf-end";
const right = activeDocument.createElement("span");
right.className = "mpdf-hf-right";
right.textContent = layout.footerRight;
footer.appendChild(right);
}
@ -1779,21 +1776,17 @@ class PDFExportSettingTab extends PluginSettingTab {
.setDesc("Pick a preset to configure the overall document style. Fine-tune any setting below.")
.addDropdown((d) => {
Object.entries(PRESETS).forEach(([k, v]) => d.addOption(k, v.name));
d.setValue(s.preset).onChange(async (v) => {
d.setValue(s.preset).onChange((v) => {
this.plugin.applyPreset(v);
await this.markDirty();
// Refresh the form so all controls show the new preset's values,
// but do NOT call display() — that would reset the dirty flag.
this.buildSettings();
void this.markDirty().then(() => { this.buildSettings(); });
});
})
.addButton((b) =>
b.setButtonText("Reset Preset")
.setTooltip("Reset current preset to its default values")
.onClick(async () => {
.onClick(() => {
this.plugin.applyPreset(s.preset);
await this.markDirty();
this.buildSettings();
void this.markDirty().then(() => { this.buildSettings(); });
}),
);
@ -1801,27 +1794,28 @@ class PDFExportSettingTab extends PluginSettingTab {
new Setting(containerEl).setName("Page").setHeading();
new Setting(containerEl).setName("Page size").addDropdown((d) => {
Object.keys(PAGE_SIZES).forEach((k) => d.addOption(k, k));
d.setValue(s.pageSize).onChange(async (v) => {
d.setValue(s.pageSize).onChange((v) => {
s.pageSize = v;
await this.markDirty();
void this.markDirty();
});
});
new Setting(containerEl).setName("Orientation").addDropdown((d) =>
d.addOptions({ portrait: "Portrait", landscape: "Landscape" })
.setValue(s.orientation)
.onChange(async (v) => {
.onChange((v) => {
s.orientation = v as "portrait" | "landscape";
await this.markDirty();
void this.markDirty();
}),
);
// ── Margins ───────────────────────────────────────────────────────────────
new Setting(containerEl).setName("Margins (mm)").setHeading();
const marginSetting = (name: string, key: keyof PDFExportSettings) =>
type MarginKey = "marginTop" | "marginBottom" | "marginLeft" | "marginRight";
const marginSetting = (name: string, key: MarginKey) =>
new Setting(containerEl).setName(name).addText((t) =>
t.setValue(String(s[key])).onChange(async (v) => {
Object.assign(s, { [key]: parseInt(v) || 0 });
await this.markDirty();
t.setValue(String(s[key])).onChange((v) => {
s[key] = parseInt(v) || 0;
void this.markDirty();
}),
);
marginSetting("Top", "marginTop");
@ -1843,10 +1837,10 @@ class PDFExportSettingTab extends PluginSettingTab {
"'Courier New', monospace": "Courier New",
"__custom__": "Custom…",
}).setValue(s.fontFamily)
.onChange(async (v) => {
.onChange((v) => {
s.fontFamily = v;
customFontSetting.settingEl.style.display = v === "__custom__" ? "" : "none";
await this.markDirty();
customFontSetting.settingEl.toggleClass("is-hidden", v !== "__custom__");
void this.markDirty();
}),
);
customFontSetting = new Setting(containerEl)
@ -1855,30 +1849,30 @@ class PDFExportSettingTab extends PluginSettingTab {
.addText((t) =>
t.setPlaceholder("e.g. Inter, sans-serif")
.setValue(s.customFontName)
.onChange(async (v) => { s.customFontName = v; await this.markDirty(); }),
.onChange((v) => { s.customFontName = v; void this.markDirty(); }),
);
customFontSetting.settingEl.style.display = s.fontFamily === "__custom__" ? "" : "none";
customFontSetting.settingEl.toggleClass("is-hidden", s.fontFamily !== "__custom__");
new Setting(containerEl).setName("Font size (px)").addDropdown((d) => {
["10","11","12","13","14","15","16"].forEach((v) => d.addOption(v, v + "px"));
d.setValue(String(s.fontSize)).onChange(async (v) => {
d.setValue(String(s.fontSize)).onChange((v) => {
s.fontSize = parseInt(v);
await this.markDirty();
void this.markDirty();
});
});
new Setting(containerEl).setName("Code font size").addDropdown((d) =>
d.addOptions({ "0.75": "0.75em", "0.80": "0.80em", "0.82": "0.82em", "0.85": "0.85em", "0.88": "0.88em", "0.90": "0.90em", "1.0": "1.00em" })
.setValue(String(s.codeFontSize))
.onChange(async (v) => { s.codeFontSize = parseFloat(v); await this.markDirty(); }),
.onChange((v) => { s.codeFontSize = parseFloat(v); void this.markDirty(); }),
);
new Setting(containerEl).setName("Line height").addDropdown((d) =>
d.addOptions({ "1.4": "Tight (1.4)", "1.6": "Compact (1.6)", "1.75": "Normal (1.75)", "1.85": "Relaxed (1.85)", "2.0": "Double (2.0)" })
.setValue(String(s.lineHeight))
.onChange(async (v) => { s.lineHeight = parseFloat(v); await this.markDirty(); }),
.onChange((v) => { s.lineHeight = parseFloat(v); void this.markDirty(); }),
);
new Setting(containerEl).setName("Paragraph spacing").addDropdown((d) =>
d.addOptions({ "0": "None", "0.3": "Tight (0.3em)", "0.5": "Normal (0.5em)", "0.65": "Relaxed (0.65em)", "1.0": "Wide (1em)" })
.setValue(String(s.paragraphSpacing))
.onChange(async (v) => { s.paragraphSpacing = parseFloat(v); await this.markDirty(); }),
.onChange((v) => { s.paragraphSpacing = parseFloat(v); void this.markDirty(); }),
);
new Setting(containerEl)
.setName("Heading scale")
@ -1886,16 +1880,18 @@ class PDFExportSettingTab extends PluginSettingTab {
.addDropdown((d) =>
d.addOptions({ "0.8": "Small (0.8×)", "0.88": "0.88×", "0.9": "Compact (0.9×)", "0.95": "0.95×", "1.0": "Normal (1.0×)", "1.05": "1.05×", "1.1": "Large (1.1×)", "1.2": "XLarge (1.2×)" })
.setValue(String(s.headingScale))
.onChange(async (v) => { s.headingScale = parseFloat(v); await this.markDirty(); }),
.onChange((v) => { s.headingScale = parseFloat(v); void this.markDirty(); }),
);
// ── Colors ────────────────────────────────────────────────────────────────
new Setting(containerEl).setName("Colors").setHeading();
const colorSetting = (name: string, key: keyof PDFExportSettings) =>
type ColorKey = "accentColor" | "bodyColor" | "headingColor" | "pageBackground"
| "blockquoteBg" | "blockquoteBorderColor" | "tableHeaderBg" | "codeBackground";
const colorSetting = (name: string, key: ColorKey) =>
new Setting(containerEl).setName(name).addColorPicker((cp) =>
cp.setValue(String(s[key])).onChange(async (v) => {
Object.assign(s, { [key]: v });
await this.markDirty();
cp.setValue(s[key]).onChange((v) => {
s[key] = v;
void this.markDirty();
}),
);
colorSetting("Accent color", "accentColor");
@ -1910,70 +1906,70 @@ class PDFExportSettingTab extends PluginSettingTab {
// ── Heading style ─────────────────────────────────────────────────────────
new Setting(containerEl).setName("Heading Style").setHeading();
new Setting(containerEl).setName("H1 bottom border").addToggle((t) =>
t.setValue(s.h1BorderBottom).onChange(async (v) => { s.h1BorderBottom = v; await this.markDirty(); }),
t.setValue(s.h1BorderBottom).onChange((v) => { s.h1BorderBottom = v; void this.markDirty(); }),
);
new Setting(containerEl).setName("H2 bottom border").addToggle((t) =>
t.setValue(s.h2BorderBottom).onChange(async (v) => { s.h2BorderBottom = v; await this.markDirty(); }),
t.setValue(s.h2BorderBottom).onChange((v) => { s.h2BorderBottom = v; void this.markDirty(); }),
);
new Setting(containerEl).setName("Center H1").addToggle((t) =>
t.setValue(s.centerH1).onChange(async (v) => { s.centerH1 = v; await this.markDirty(); }),
t.setValue(s.centerH1).onChange((v) => { s.centerH1 = v; void this.markDirty(); }),
);
// ── Tables ────────────────────────────────────────────────────────────────
new Setting(containerEl).setName("Tables").setHeading();
new Setting(containerEl).setName("Striped rows").addToggle((t) =>
t.setValue(s.tableStriped).onChange(async (v) => { s.tableStriped = v; await this.markDirty(); }),
t.setValue(s.tableStriped).onChange((v) => { s.tableStriped = v; void this.markDirty(); }),
);
// ── Header & Footer ───────────────────────────────────────────────────────
new Setting(containerEl).setName("Header & Footer").setHeading();
new Setting(containerEl).setName("Show header").addToggle((t) =>
t.setValue(s.showHeader).onChange(async (v) => { s.showHeader = v; await this.markDirty(); }),
t.setValue(s.showHeader).onChange((v) => { s.showHeader = v; void this.markDirty(); }),
);
new Setting(containerEl)
.setName("Header text")
.setDesc("Appears on every page according to the chosen alignment.")
.addText((t) => t.setValue(s.headerText).onChange(async (v) => { s.headerText = v; await this.markDirty(); }));
.addText((t) => t.setValue(s.headerText).onChange((v) => { s.headerText = v; void this.markDirty(); }));
new Setting(containerEl).setName("Header alignment").addDropdown((d) =>
d.addOptions({ left: "Left", center: "Center", right: "Right" })
.setValue(s.headerAlignment)
.onChange(async (v) => { s.headerAlignment = v as "left"|"center"|"right"; await this.markDirty(); }),
.onChange((v) => { s.headerAlignment = v as "left"|"center"|"right"; void this.markDirty(); }),
);
new Setting(containerEl).setName("Show footer").addToggle((t) =>
t.setValue(s.showFooter).onChange(async (v) => { s.showFooter = v; await this.markDirty(); }),
t.setValue(s.showFooter).onChange((v) => { s.showFooter = v; void this.markDirty(); }),
);
new Setting(containerEl).setName("Footer border").setDesc("Show the separator line above the footer.").addToggle((t) =>
t.setValue(s.showFooterBorder).onChange(async (v) => { s.showFooterBorder = v; await this.markDirty(); }),
t.setValue(s.showFooterBorder).onChange((v) => { s.showFooterBorder = v; void this.markDirty(); }),
);
new Setting(containerEl)
.setName("Footer text")
.addText((t) => t.setValue(s.footerText).onChange(async (v) => { s.footerText = v; await this.markDirty(); }));
.addText((t) => t.setValue(s.footerText).onChange((v) => { s.footerText = v; void this.markDirty(); }));
new Setting(containerEl).setName("Show page numbers").addToggle((t) =>
t.setValue(s.showPageNumbers).onChange(async (v) => { s.showPageNumbers = v; await this.markDirty(); }),
t.setValue(s.showPageNumbers).onChange((v) => { s.showPageNumbers = v; void this.markDirty(); }),
);
new Setting(containerEl).setName("Page number position").addDropdown((d) =>
d.addOptions({ left: "Left", center: "Center", right: "Right" })
.setValue(s.pageNumberPosition)
.onChange(async (v) => { s.pageNumberPosition = v as "left"|"center"|"right"; await this.markDirty(); }),
.onChange((v) => { s.pageNumberPosition = v as "left"|"center"|"right"; void this.markDirty(); }),
);
new Setting(containerEl)
.setName("Page number start")
.setDesc("Number assigned to the first visible page number. Accepts any integer.")
.addText((t) =>
t.setValue(String(s.pageNumberStart))
.onChange(async (v) => {
.onChange((v) => {
const n = parseInt(v, 10);
s.pageNumberStart = isNaN(n) ? 1 : n;
await this.markDirty();
void this.markDirty();
}),
);
new Setting(containerEl)
.setName("Header/footer on first page")
.setDesc("When off, page 1 has no header, footer, or page number. Numbering begins on page 2 using the start value.")
.addToggle((t) =>
t.setValue(s.showHeaderFooterOnFirstPage).onChange(async (v) => {
t.setValue(s.showHeaderFooterOnFirstPage).onChange((v) => {
s.showHeaderFooterOnFirstPage = v;
await this.markDirty();
void this.markDirty();
}),
);
@ -1983,9 +1979,9 @@ class PDFExportSettingTab extends PluginSettingTab {
.setName("Hide frontmatter")
.setDesc("Strip the YAML frontmatter block (--- … ---) from the preview and exported PDF.")
.addToggle((t) =>
t.setValue(s.hideFrontmatter).onChange(async (v) => {
t.setValue(s.hideFrontmatter).onChange((v) => {
s.hideFrontmatter = v;
await this.markDirty();
void this.markDirty();
}),
);
new Setting(containerEl)
@ -1995,16 +1991,16 @@ class PDFExportSettingTab extends PluginSettingTab {
"Mirrors Obsidian's built-in 'Include file name as title' export option.",
)
.addToggle((t) =>
t.setValue(s.includeFilenameAsTitle).onChange(async (v) => {
t.setValue(s.includeFilenameAsTitle).onChange((v) => {
s.includeFilenameAsTitle = v;
await this.markDirty();
void this.markDirty();
}),
);
new Setting(containerEl).setName("Auto page break before H1").addToggle((t) =>
t.setValue(s.autoBreakH1).onChange(async (v) => { s.autoBreakH1 = v; await this.markDirty(); }),
t.setValue(s.autoBreakH1).onChange((v) => { s.autoBreakH1 = v; void this.markDirty(); }),
);
new Setting(containerEl).setName("Auto page break before H2").addToggle((t) =>
t.setValue(s.autoBreakH2).onChange(async (v) => { s.autoBreakH2 = v; await this.markDirty(); }),
t.setValue(s.autoBreakH2).onChange((v) => { s.autoBreakH2 = v; void this.markDirty(); }),
);
}
}

312
package-lock.json generated
View file

@ -8,11 +8,11 @@
"name": "obsidian-advanced-pdf-export",
"version": "3.0.0",
"devDependencies": {
"@types/node": "^16.11.6",
"esbuild": "0.17.3",
"@types/node": "^22.14.0",
"esbuild": "0.25.5",
"obsidian": "latest",
"tslib": "2.4.0",
"typescript": "4.7.4"
"tslib": "2.8.1",
"typescript": "5.8.3"
}
},
"node_modules/@codemirror/state": {
@ -36,10 +36,27 @@
"w3c-keyname": "^2.2.4"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz",
"integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.17.3",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.3.tgz",
"integrity": "sha512-1Mlz934GvbgdDmt26rTLmf03cAgLg5HyOgJN+ZGCeP3Q9ynYTNMn2/LQxIl7Uy+o4K6Rfi2OuLsr12JQQR8gNg==",
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz",
"integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==",
"cpu": [
"arm"
],
@ -50,13 +67,13 @@
"android"
],
"engines": {
"node": ">=12"
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.17.3",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.3.tgz",
"integrity": "sha512-XvJsYo3dO3Pi4kpalkyMvfQsjxPWHYjoX4MDiB/FUM4YMfWcXa5l4VCwFWVYI1+92yxqjuqrhNg0CZg3gSouyQ==",
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz",
"integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==",
"cpu": [
"arm64"
],
@ -67,13 +84,13 @@
"android"
],
"engines": {
"node": ">=12"
"node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.17.3",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.3.tgz",
"integrity": "sha512-nuV2CmLS07Gqh5/GrZLuqkU9Bm6H6vcCspM+zjp9TdQlxJtIe+qqEXQChmfc7nWdyr/yz3h45Utk1tUn8Cz5+A==",
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz",
"integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==",
"cpu": [
"x64"
],
@ -84,13 +101,13 @@
"android"
],
"engines": {
"node": ">=12"
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.17.3",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.3.tgz",
"integrity": "sha512-01Hxaaat6m0Xp9AXGM8mjFtqqwDjzlMP0eQq9zll9U85ttVALGCGDuEvra5Feu/NbP5AEP1MaopPwzsTcUq1cw==",
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz",
"integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==",
"cpu": [
"arm64"
],
@ -101,13 +118,13 @@
"darwin"
],
"engines": {
"node": ">=12"
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.17.3",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.3.tgz",
"integrity": "sha512-Eo2gq0Q/er2muf8Z83X21UFoB7EU6/m3GNKvrhACJkjVThd0uA+8RfKpfNhuMCl1bKRfBzKOk6xaYKQZ4lZqvA==",
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz",
"integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==",
"cpu": [
"x64"
],
@ -118,13 +135,13 @@
"darwin"
],
"engines": {
"node": ">=12"
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.17.3",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.3.tgz",
"integrity": "sha512-CN62ESxaquP61n1ZjQP/jZte8CE09M6kNn3baos2SeUfdVBkWN5n6vGp2iKyb/bm/x4JQzEvJgRHLGd5F5b81w==",
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz",
"integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==",
"cpu": [
"arm64"
],
@ -135,13 +152,13 @@
"freebsd"
],
"engines": {
"node": ">=12"
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.17.3",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.3.tgz",
"integrity": "sha512-feq+K8TxIznZE+zhdVurF3WNJ/Sa35dQNYbaqM/wsCbWdzXr5lyq+AaTUSER2cUR+SXPnd/EY75EPRjf4s1SLg==",
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz",
"integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==",
"cpu": [
"x64"
],
@ -152,13 +169,13 @@
"freebsd"
],
"engines": {
"node": ">=12"
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.17.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.3.tgz",
"integrity": "sha512-CLP3EgyNuPcg2cshbwkqYy5bbAgK+VhyfMU7oIYyn+x4Y67xb5C5ylxsNUjRmr8BX+MW3YhVNm6Lq6FKtRTWHQ==",
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz",
"integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==",
"cpu": [
"arm"
],
@ -169,13 +186,13 @@
"linux"
],
"engines": {
"node": ">=12"
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.17.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.3.tgz",
"integrity": "sha512-JHeZXD4auLYBnrKn6JYJ0o5nWJI9PhChA/Nt0G4MvLaMrvXuWnY93R3a7PiXeJQphpL1nYsaMcoV2QtuvRnF/g==",
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz",
"integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==",
"cpu": [
"arm64"
],
@ -186,13 +203,13 @@
"linux"
],
"engines": {
"node": ">=12"
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.17.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.3.tgz",
"integrity": "sha512-FyXlD2ZjZqTFh0sOQxFDiWG1uQUEOLbEh9gKN/7pFxck5Vw0qjWSDqbn6C10GAa1rXJpwsntHcmLqydY9ST9ZA==",
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz",
"integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==",
"cpu": [
"ia32"
],
@ -203,13 +220,13 @@
"linux"
],
"engines": {
"node": ">=12"
"node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.17.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.3.tgz",
"integrity": "sha512-OrDGMvDBI2g7s04J8dh8/I7eSO+/E7nMDT2Z5IruBfUO/RiigF1OF6xoH33Dn4W/OwAWSUf1s2nXamb28ZklTA==",
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz",
"integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==",
"cpu": [
"loong64"
],
@ -220,13 +237,13 @@
"linux"
],
"engines": {
"node": ">=12"
"node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.17.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.3.tgz",
"integrity": "sha512-DcnUpXnVCJvmv0TzuLwKBC2nsQHle8EIiAJiJ+PipEVC16wHXaPEKP0EqN8WnBe0TPvMITOUlP2aiL5YMld+CQ==",
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz",
"integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==",
"cpu": [
"mips64el"
],
@ -237,13 +254,13 @@
"linux"
],
"engines": {
"node": ">=12"
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.17.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.3.tgz",
"integrity": "sha512-BDYf/l1WVhWE+FHAW3FzZPtVlk9QsrwsxGzABmN4g8bTjmhazsId3h127pliDRRu5674k1Y2RWejbpN46N9ZhQ==",
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz",
"integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==",
"cpu": [
"ppc64"
],
@ -254,13 +271,13 @@
"linux"
],
"engines": {
"node": ">=12"
"node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.17.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.3.tgz",
"integrity": "sha512-WViAxWYMRIi+prTJTyV1wnqd2mS2cPqJlN85oscVhXdb/ZTFJdrpaqm/uDsZPGKHtbg5TuRX/ymKdOSk41YZow==",
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz",
"integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==",
"cpu": [
"riscv64"
],
@ -271,13 +288,13 @@
"linux"
],
"engines": {
"node": ">=12"
"node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.17.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.3.tgz",
"integrity": "sha512-Iw8lkNHUC4oGP1O/KhumcVy77u2s6+KUjieUqzEU3XuWJqZ+AY7uVMrrCbAiwWTkpQHkr00BuXH5RpC6Sb/7Ug==",
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz",
"integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==",
"cpu": [
"s390x"
],
@ -288,11 +305,13 @@
"linux"
],
"engines": {
"node": ">=12"
"node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.17.3",
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz",
"integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==",
"cpu": [
"x64"
],
@ -303,13 +322,30 @@
"linux"
],
"engines": {
"node": ">=12"
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz",
"integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.17.3",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.3.tgz",
"integrity": "sha512-4+rR/WHOxIVh53UIQIICryjdoKdHsFZFD4zLSonJ9RRw7bhKzVyXbnRPsWSfwybYqw9sB7ots/SYyufL1mBpEg==",
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz",
"integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==",
"cpu": [
"x64"
],
@ -320,13 +356,30 @@
"netbsd"
],
"engines": {
"node": ">=12"
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz",
"integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.17.3",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.3.tgz",
"integrity": "sha512-cVpWnkx9IYg99EjGxa5Gc0XmqumtAwK3aoz7O4Dii2vko+qXbkHoujWA68cqXjhh6TsLaQelfDO4MVnyr+ODeA==",
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz",
"integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==",
"cpu": [
"x64"
],
@ -337,13 +390,13 @@
"openbsd"
],
"engines": {
"node": ">=12"
"node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.17.3",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.3.tgz",
"integrity": "sha512-RxmhKLbTCDAY2xOfrww6ieIZkZF+KBqG7S2Ako2SljKXRFi+0863PspK74QQ7JpmWwncChY25JTJSbVBYGQk2Q==",
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz",
"integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==",
"cpu": [
"x64"
],
@ -354,13 +407,13 @@
"sunos"
],
"engines": {
"node": ">=12"
"node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.17.3",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.3.tgz",
"integrity": "sha512-0r36VeEJ4efwmofxVJRXDjVRP2jTmv877zc+i+Pc7MNsIr38NfsjkQj23AfF7l0WbB+RQ7VUb+LDiqC/KY/M/A==",
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz",
"integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==",
"cpu": [
"arm64"
],
@ -371,13 +424,13 @@
"win32"
],
"engines": {
"node": ">=12"
"node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.17.3",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.3.tgz",
"integrity": "sha512-wgO6rc7uGStH22nur4aLFcq7Wh86bE9cOFmfTr/yxN3BXvDEdCSXyKkO+U5JIt53eTOgC47v9k/C1bITWL/Teg==",
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz",
"integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==",
"cpu": [
"ia32"
],
@ -388,13 +441,13 @@
"win32"
],
"engines": {
"node": ">=12"
"node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.17.3",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.3.tgz",
"integrity": "sha512-FdVl64OIuiKjgXBjwZaJLKp0eaEckifbhn10dXWhysMJkWblg3OEEGKSIyhiD5RSgAya8WzP3DNkngtIg3Nt7g==",
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz",
"integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==",
"cpu": [
"x64"
],
@ -405,7 +458,7 @@
"win32"
],
"engines": {
"node": ">=12"
"node": ">=18"
}
},
"node_modules/@marijn/find-cluster-break": {
@ -428,9 +481,14 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "16.18.126",
"version": "22.19.20",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.20.tgz",
"integrity": "sha512-6tELRwSDYWW9EdZhbeZmYGZ1/7Djkt+Ah3/ScEYT9cDord7UJzasR/4D3VONg9tQI5CDp+/CZC1AXj2pCFOvpw==",
"dev": true,
"license": "MIT"
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
}
},
"node_modules/@types/tern": {
"version": "0.23.9",
@ -447,7 +505,9 @@
"peer": true
},
"node_modules/esbuild": {
"version": "0.17.3",
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz",
"integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@ -455,31 +515,34 @@
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=12"
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/android-arm": "0.17.3",
"@esbuild/android-arm64": "0.17.3",
"@esbuild/android-x64": "0.17.3",
"@esbuild/darwin-arm64": "0.17.3",
"@esbuild/darwin-x64": "0.17.3",
"@esbuild/freebsd-arm64": "0.17.3",
"@esbuild/freebsd-x64": "0.17.3",
"@esbuild/linux-arm": "0.17.3",
"@esbuild/linux-arm64": "0.17.3",
"@esbuild/linux-ia32": "0.17.3",
"@esbuild/linux-loong64": "0.17.3",
"@esbuild/linux-mips64el": "0.17.3",
"@esbuild/linux-ppc64": "0.17.3",
"@esbuild/linux-riscv64": "0.17.3",
"@esbuild/linux-s390x": "0.17.3",
"@esbuild/linux-x64": "0.17.3",
"@esbuild/netbsd-x64": "0.17.3",
"@esbuild/openbsd-x64": "0.17.3",
"@esbuild/sunos-x64": "0.17.3",
"@esbuild/win32-arm64": "0.17.3",
"@esbuild/win32-ia32": "0.17.3",
"@esbuild/win32-x64": "0.17.3"
"@esbuild/aix-ppc64": "0.25.5",
"@esbuild/android-arm": "0.25.5",
"@esbuild/android-arm64": "0.25.5",
"@esbuild/android-x64": "0.25.5",
"@esbuild/darwin-arm64": "0.25.5",
"@esbuild/darwin-x64": "0.25.5",
"@esbuild/freebsd-arm64": "0.25.5",
"@esbuild/freebsd-x64": "0.25.5",
"@esbuild/linux-arm": "0.25.5",
"@esbuild/linux-arm64": "0.25.5",
"@esbuild/linux-ia32": "0.25.5",
"@esbuild/linux-loong64": "0.25.5",
"@esbuild/linux-mips64el": "0.25.5",
"@esbuild/linux-ppc64": "0.25.5",
"@esbuild/linux-riscv64": "0.25.5",
"@esbuild/linux-s390x": "0.25.5",
"@esbuild/linux-x64": "0.25.5",
"@esbuild/netbsd-arm64": "0.25.5",
"@esbuild/netbsd-x64": "0.25.5",
"@esbuild/openbsd-arm64": "0.25.5",
"@esbuild/openbsd-x64": "0.25.5",
"@esbuild/sunos-x64": "0.25.5",
"@esbuild/win32-arm64": "0.25.5",
"@esbuild/win32-ia32": "0.25.5",
"@esbuild/win32-x64": "0.25.5"
}
},
"node_modules/moment": {
@ -512,12 +575,16 @@
"peer": true
},
"node_modules/tslib": {
"version": "2.4.0",
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"dev": true,
"license": "0BSD"
},
"node_modules/typescript": {
"version": "4.7.4",
"version": "5.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@ -525,9 +592,16 @@
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true,
"license": "MIT"
},
"node_modules/w3c-keyname": {
"version": "2.2.8",
"dev": true,

View file

@ -13,10 +13,10 @@
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production"
},
"devDependencies": {
"@types/node": "^16.11.6",
"esbuild": "0.17.3",
"@types/node": "^22.14.0",
"esbuild": "0.25.5",
"obsidian": "latest",
"tslib": "2.4.0",
"typescript": "4.7.4"
"tslib": "2.8.1",
"typescript": "5.8.3"
}
}

View file

@ -213,7 +213,7 @@
pointer-events: none; /* let clicks through to cancel/settings while loading */
}
.mpdf-loading-overlay--visible {
.mpdf-loading-overlay.is-active {
display: flex;
}
@ -265,3 +265,16 @@
width: 64px;
accent-color: var(--interactive-accent);
}
/* Utility: toggle visibility without affecting layout flow */
.is-hidden {
display: none !important;
}
/* Static positioning for the per-page shadow-DOM host; width/height/transform set inline */
.mpdf-shadow-host {
position: absolute;
top: 0;
left: 0;
transform-origin: top left;
}

View file

@ -4,17 +4,14 @@
"inlineSourceMap": true,
"inlineSources": true,
"module": "ESNext",
"target": "ES2017",
"target": "ES2022",
"allowSyntheticDefaultImports": true,
"moduleResolution": "node",
"importHelpers": true,
"isolatedModules": true,
"strictNullChecks": true,
"strictPropertyInitialization": false,
"ignoreDeprecations": "6.0",
"types": ["node"],
"lib": ["ES2017", "DOM"]
"skipLibCheck": true,
"lib": ["ES2022", "DOM", "DOM.Iterable"]
},
"include": ["**/*.ts"]
}