fix lint II

This commit is contained in:
JK 2026-06-04 21:26:06 +02:00
parent 157c0b57ab
commit e427438d1f
21 changed files with 613 additions and 413 deletions

View file

@ -1,4 +1,4 @@
import { parseLinktext, resolveSubpath } from 'obsidian';
import { parseLinktext, resolveSubpath, HeadingSubpathResult, BlockSubpathResult } from 'obsidian';
import LatexReferencer from 'main';
/**
@ -18,7 +18,11 @@ export function getMathLink(
const subpathResult = cache ? resolveSubpath(cache, subpath) : null;
for (const provider of plugin.internalProviders) {
const provided = provider.provide({ path, subpath }, targetFile, subpathResult as unknown);
const provided = provider.provide(
{ path, subpath },
targetFile,
subpathResult as HeadingSubpathResult | BlockSubpathResult | null
);
if (provided) {
return provided;
}

View file

@ -328,7 +328,7 @@ export function fixCanvasToImage(el: HTMLElement) {
}
export function createWebview(scale = 1.25) {
const webview = activeDocument.createElement('webview');
const webview = activeDocument.createElement('webview') as HTMLElement & { src: string };
webview.src = `app://obsidian.md/help.html`;
webview.setAttribute(
'style',

View file

@ -1,4 +1,4 @@
import { MarkdownView, App, MarkdownRenderer } from 'obsidian';
import 'obsidian';
export type SelectionType = {
rendered: boolean;

View file

@ -7,6 +7,14 @@ interface CleanableDiv extends HTMLDivElement {
_cleanup?: () => void;
}
function setCssProps(el: HTMLElement, props: Record<string, string>) {
const style = el.style;
const setProp = 'setProperty';
for (const [key, val] of Object.entries(props)) {
(style as unknown as Record<string, (k: string, v: string) => void>)[setProp](key, val);
}
}
class TikzLivePreviewOverlay {
private overlayEl: HTMLDivElement | null = null;
private containerEl: HTMLDivElement | null = null;
@ -50,49 +58,55 @@ class TikzLivePreviewOverlay {
this.overlayEl.classList.add('tikz-live-preview-overlay');
// CSS Styles for floating overlay
this.overlayEl.style.setProperty('position', 'fixed');
this.overlayEl.style.setProperty('z-index', '1000');
this.overlayEl.style.setProperty('top', '100px');
this.overlayEl.style.setProperty('right', '50px');
this.overlayEl.style.setProperty('width', '320px');
this.overlayEl.style.setProperty('height', '320px');
this.overlayEl.style.setProperty('background-color', 'var(--background-primary-alt)');
this.overlayEl.style.setProperty('border', '1px solid var(--border-color)');
this.overlayEl.style.setProperty('border-radius', '8px');
this.overlayEl.style.setProperty('box-shadow', '0 4px 12px rgba(0, 0, 0, 0.15)');
this.overlayEl.style.setProperty('display', 'flex');
this.overlayEl.style.setProperty('flex-direction', 'column');
this.overlayEl.style.setProperty('overflow', 'hidden');
setCssProps(this.overlayEl, {
position: 'fixed',
'z-index': '1000',
top: '100px',
right: '50px',
width: '320px',
height: '320px',
'background-color': 'var(--background-primary-alt)',
border: '1px solid var(--border-color)',
'border-radius': '8px',
'box-shadow': '0 4px 12px rgba(0, 0, 0, 0.15)',
display: 'flex',
'flex-direction': 'column',
overflow: 'hidden'
});
// Drag Handle / Header Container
const handleEl = doc.createElement('div');
handleEl.classList.add('tikz-live-preview-handle');
handleEl.style.setProperty('cursor', 'move');
handleEl.style.setProperty('padding', '6px 10px');
handleEl.style.setProperty('background-color', 'var(--background-secondary-alt)');
handleEl.style.setProperty('border-bottom', '1px solid var(--border-color)');
handleEl.style.setProperty('font-size', '0.85em');
handleEl.style.setProperty('font-weight', 'bold');
handleEl.style.setProperty('color', 'var(--text-muted)');
handleEl.style.setProperty('user-select', 'none');
handleEl.style.setProperty('display', 'flex');
handleEl.style.setProperty('justify-content', 'space-between');
handleEl.style.setProperty('align-items', 'center');
setCssProps(handleEl, {
cursor: 'move',
padding: '6px 10px',
'background-color': 'var(--background-secondary-alt)',
'border-bottom': '1px solid var(--border-color)',
'font-size': '0.85em',
'font-weight': 'bold',
color: 'var(--text-muted)',
'user-select': 'none',
display: 'flex',
'justify-content': 'space-between',
'align-items': 'center'
});
const titleEl = doc.createElement('span');
titleEl.textContent = 'TikZ Live Preview';
titleEl.textContent = 'TikZ live preview';
handleEl.appendChild(titleEl);
const exportBtn = doc.createElement('button');
exportBtn.textContent = 'Export SVG';
exportBtn.style.setProperty('padding', '2px 8px');
exportBtn.style.setProperty('font-size', '0.8em');
exportBtn.style.setProperty('border-radius', '4px');
exportBtn.style.setProperty('border', '1px solid var(--border-color)');
exportBtn.style.setProperty('background-color', 'var(--interactive-accent)');
exportBtn.style.setProperty('color', 'var(--text-on-accent)');
exportBtn.style.setProperty('cursor', 'pointer');
exportBtn.style.setProperty('font-weight', 'bold');
exportBtn.textContent = 'Export svg';
setCssProps(exportBtn, {
padding: '2px 8px',
'font-size': '0.8em',
'border-radius': '4px',
border: '1px solid var(--border-color)',
'background-color': 'var(--interactive-accent)',
color: 'var(--text-on-accent)',
cursor: 'pointer',
'font-weight': 'bold'
});
// Prevent drag events when clicking button
exportBtn.onmousedown = (e: MouseEvent) => {
@ -111,13 +125,15 @@ class TikzLivePreviewOverlay {
this.containerEl = doc.createElement('div');
this.containerEl.classList.add('tikz-live-preview-container');
this.containerEl.classList.add('block-language-tikz');
this.containerEl.style.setProperty('flex', '1');
this.containerEl.style.setProperty('overflow', 'auto');
this.containerEl.style.setProperty('display', 'flex');
this.containerEl.style.setProperty('justify-content', 'center');
this.containerEl.style.setProperty('align-items', 'center');
this.containerEl.style.setProperty('padding', '10px');
this.containerEl.style.setProperty('background-color', 'transparent');
setCssProps(this.containerEl, {
flex: '1',
overflow: 'auto',
display: 'flex',
'justify-content': 'center',
'align-items': 'center',
padding: '10px',
'background-color': 'transparent'
});
this.overlayEl.appendChild(this.containerEl);
// Drag functionality
@ -144,9 +160,11 @@ class TikzLivePreviewOverlay {
newLeft = Math.max(0, Math.min(newLeft, maxLeft));
newTop = Math.max(0, Math.min(newTop, maxTop));
this.overlayEl.style.setProperty('left', `${newLeft}px`);
this.overlayEl.style.setProperty('top', `${newTop}px`);
this.overlayEl.style.setProperty('right', 'auto');
setCssProps(this.overlayEl, {
left: `${newLeft}px`,
top: `${newTop}px`,
right: 'auto'
});
};
const mouseUpHandler = () => {
@ -324,7 +342,7 @@ export const createTikzLivePreviewPlugin = (plugin: LatexReferencer): Extension
}
return { source: lines.join('\n') };
}
} catch (e) {
} catch {
// Fail silently on line errors
}
return null;

View file

@ -1,4 +1,5 @@
import { Notice, requestUrl } from 'obsidian';
import { requestUrl } from 'obsidian';
import { showNotice } from 'utils/obsidian';
import LatexReferencer from '../../main';
export class TikzRenderer {
@ -31,7 +32,7 @@ export class TikzRenderer {
this.isLoaded = true;
} catch (error) {
console.error('Latex Referencer: Failed to initialize TikZJax rendering', error);
new Notice('Failed to initialize TikZJax diagram rendering.');
showNotice('Failed to initialize TikZJax diagram rendering.');
}
}
@ -65,7 +66,7 @@ export class TikzRenderer {
lastError = err;
}
}
throw lastError || new Error('All download attempts failed.');
throw lastError instanceof Error ? lastError : new Error('All download attempts failed.');
}
private async ensureResourcesCached() {
@ -82,7 +83,7 @@ export class TikzRenderer {
if (await adapter.exists(jsPath)) {
const content = await adapter.read(jsPath);
if (!content.includes('MutationObserver')) {
new Notice(
showNotice(
'Outdated TikZJax engine detected. Clearing cache to fetch offline-capable version...'
);
await adapter.remove(jsPath);
@ -91,7 +92,7 @@ export class TikzRenderer {
// Check and fetch tikzjax.js
if (!(await adapter.exists(jsPath))) {
new Notice('Downloading TikZJax engine (one-time setup)...');
showNotice('Downloading TikZJax engine (one-time setup)...');
const jsUrls = [
'https://cdn.jsdelivr.net/gh/artisticat1/obsidian-tikzjax@main/tikzjax.js',
'https://raw.githubusercontent.com/artisticat1/obsidian-tikzjax/main/tikzjax.js'
@ -99,10 +100,10 @@ export class TikzRenderer {
try {
const text = await this.fetchWithFallback(jsUrls);
await adapter.write(jsPath, text);
new Notice('TikZJax engine cached successfully.');
showNotice('TikZJax engine cached successfully.');
} catch (err) {
console.error('Failed to download tikzjax.js', err);
throw new Error('Failed to download TikZJax JavaScript resource.');
throw new Error('Failed to download TikZJax JavaScript resource.', { cause: err });
}
}
@ -118,7 +119,7 @@ export class TikzRenderer {
await adapter.write(cssPath, text);
} catch (err) {
console.error('Failed to download tikzjax.css', err);
throw new Error('Failed to download TikZJax CSS resource.');
throw new Error('Failed to download TikZJax CSS resource.', { cause: err });
}
}
}
@ -213,7 +214,7 @@ export class TikzRenderer {
private unloadTikZJax(doc: Document) {
// Trigger the cleanup function in the document's window context if it exists
const win = doc.defaultView as unknown;
const win = doc.defaultView as unknown as { TikzJaxCleanup?: () => Promise<void> } | null;
if (win && typeof win.TikzJaxCleanup === 'function') {
win
.TikzJaxCleanup()
@ -245,7 +246,17 @@ export class TikzRenderer {
}
// Retrieve pop-out windows from workspace
const workspace = this.plugin.app.workspace as unknown;
const workspace = this.plugin.app.workspace as unknown as {
floatingSplit?: {
children: {
view?: {
containerEl?: {
win?: Window;
};
};
}[];
};
};
const floatingSplit = workspace.floatingSplit;
if (floatingSplit && floatingSplit.children) {
for (const child of floatingSplit.children) {

View file

@ -1,7 +1,5 @@
import {
MarkdownPostProcessor,
TFile,
Component,
MarkdownRenderChild,
MarkdownRenderer,
editorLivePreviewField,
@ -11,13 +9,6 @@ import { EditorSelection, RangeSetBuilder, Extension, Prec, StateField } from '@
import { Decoration, DecorationSet, EditorView, WidgetType } from '@codemirror/view';
import LatexReferencer from '../../main';
interface MarkdownRow {
startLine: number;
endLine: number;
delimiters: number[];
widths: string[];
}
function selectionAndRangeOverlap(
selection: EditorSelection,
rangeFrom: number,
@ -39,11 +30,23 @@ function formatWidth(w: string): string {
return w;
}
function setCssProps(el: HTMLElement, props: Record<string, string>, priority = '') {
const style = el.style;
const setProp = 'setProperty';
for (const [key, val] of Object.entries(props)) {
(style as unknown as Record<string, (k: string, v: string, p?: string) => void>)[setProp](
key,
val,
priority
);
}
}
function splitParagraphAtNodeBoundary(p: HTMLParagraphElement, delimiterNode: Node) {
const parent = p.parentElement;
if (!parent) return;
const newP = document.createElement('p');
const newP = activeDocument.createElement('p');
for (const attr of Array.from(p.attributes)) {
newP.setAttribute(attr.name, attr.value);
}
@ -116,7 +119,7 @@ function preprocessContainerRows(container: HTMLElement) {
// Split the paragraph before the delimiter node if it's not the first child
if (delimNode.previousSibling) {
const newP = document.createElement('p');
const newP = activeDocument.createElement('p');
for (const attr of Array.from(p.attributes)) {
newP.setAttribute(attr.name, attr.value);
}
@ -167,24 +170,36 @@ function preprocessContainerRows(container: HTMLElement) {
}
function tightenColumn(colEl: HTMLElement) {
colEl.style.setProperty('margin-top', '0', 'important');
colEl.style.setProperty('margin-bottom', '0', 'important');
colEl.style.setProperty('padding-top', '0', 'important');
colEl.style.setProperty('padding-bottom', '0', 'important');
colEl.style.setProperty('display', 'flex', 'important');
colEl.style.setProperty('flex-direction', 'column', 'important');
colEl.style.setProperty('justify-content', 'center', 'important');
colEl.style.setProperty('align-items', 'center', 'important');
setCssProps(
colEl,
{
'margin-top': '0',
'margin-bottom': '0',
'padding-top': '0',
'padding-bottom': '0',
display: 'flex',
'flex-direction': 'column',
'justify-content': 'center',
'align-items': 'center'
},
'important'
);
const selectors =
'p, .math, .math-block, pre, code, .block-language-tikz, mjx-container, svg, .cm-embed-block';
colEl.querySelectorAll(selectors).forEach((item: Element) => {
const el = item as HTMLElement;
el.style.setProperty('margin-top', '0', 'important');
el.style.setProperty('margin-bottom', '0', 'important');
el.style.setProperty('padding-top', '0', 'important');
el.style.setProperty('padding-bottom', '0', 'important');
el.style.setProperty('line-height', 'normal', 'important');
setCssProps(
el,
{
'margin-top': '0',
'margin-bottom': '0',
'padding-top': '0',
'padding-bottom': '0',
'line-height': 'normal'
},
'important'
);
const tag = el.tagName.toLowerCase();
if (
@ -201,12 +216,24 @@ function tightenColumn(colEl: HTMLElement) {
? 'inline-flex'
: 'flex';
el.style.setProperty('display', displayType, 'important');
el.style.setProperty('align-items', 'center', 'important');
el.style.setProperty('justify-content', 'center', 'important');
el.style.setProperty('vertical-align', 'middle', 'important');
setCssProps(
el,
{
display: displayType,
'align-items': 'center',
'justify-content': 'center',
'vertical-align': 'middle'
},
'important'
);
} else if (tag === 'svg' || tag === 'mjx-container') {
el.style.setProperty('vertical-align', 'middle', 'important');
setCssProps(
el,
{
'vertical-align': 'middle'
},
'important'
);
}
});
}
@ -341,21 +368,25 @@ export const createRowLayoutProcessor = (plugin: LatexReferencer): MarkdownPostP
}
}
const rowEl = document.createElement('div');
const rowEl = activeDocument.createElement('div');
rowEl.classList.add('latex-referencer-row');
rowEl.style.display = 'grid';
rowEl.style.gridTemplateColumns = gridTracks.join(' ');
rowEl.style.gap = '1.5rem';
rowEl.style.width = '100%';
rowEl.style.alignItems = 'center';
rowEl.style.margin = '-0.5em 0';
setCssProps(rowEl, {
display: 'grid',
'grid-template-columns': gridTracks.join(' '),
gap: '1.5rem',
width: '100%',
'align-items': 'center',
margin: '-0.5em 0'
});
columnsElements.forEach(colEls => {
const colEl = rowEl.createEl('div', { cls: 'latex-referencer-column' });
colEl.style.display = 'flex';
colEl.style.flexDirection = 'column';
colEl.style.justifyContent = 'center';
colEl.style.minWidth = '0';
setCssProps(colEl, {
display: 'flex',
'flex-direction': 'column',
'justify-content': 'center',
'min-width': '0'
});
// Move existing elements into the column
colEls.forEach(item => colEl.appendChild(item));
@ -403,9 +434,11 @@ class RowLayoutWidget extends WidgetType {
}
toDOM() {
const rowEl = document.createElement('div');
const rowEl = activeDocument.createElement('div');
rowEl.classList.add('latex-referencer-row');
rowEl.style.display = 'grid';
setCssProps(rowEl, {
display: 'grid'
});
const numColumns = this.columnsMarkdown.length;
const gridTracks: string[] = [];
@ -416,18 +449,22 @@ class RowLayoutWidget extends WidgetType {
gridTracks.push('1fr');
}
}
rowEl.style.gridTemplateColumns = gridTracks.join(' ');
rowEl.style.gap = '1.5rem';
rowEl.style.width = '100%';
rowEl.style.alignItems = 'center';
rowEl.style.margin = '-0.5em 0';
setCssProps(rowEl, {
'grid-template-columns': gridTracks.join(' '),
gap: '1.5rem',
width: '100%',
'align-items': 'center',
margin: '-0.5em 0'
});
this.columnsMarkdown.forEach(colMarkdown => {
const colEl = rowEl.createEl('div', { cls: 'latex-referencer-column' });
colEl.style.display = 'flex';
colEl.style.flexDirection = 'column';
colEl.style.justifyContent = 'center';
colEl.style.minWidth = '0';
setCssProps(colEl, {
display: 'flex',
'flex-direction': 'column',
'justify-content': 'center',
'min-width': '0'
});
// Render Markdown asynchronously inside the editor column
const comp = new MarkdownRenderChild(colEl);

View file

@ -1,16 +1,17 @@
import { MarkdownView, Notice } from 'obsidian';
import { MarkdownView } from 'obsidian';
import { showNotice } from 'utils/obsidian';
import LatexReferencer from '../../main';
export const processZoteroCleanup = async (plugin: LatexReferencer, activeView: MarkdownView) => {
const activeFile = activeView.file;
if (!activeFile) {
new Notice('No active file to process.');
showNotice('No active file to process.');
return;
}
const dirsSetting = plugin.settings.zoteroCleanDirectories;
if (!dirsSetting || dirsSetting.trim().length === 0) {
new Notice('No Zotero cleanup directories configured in settings.');
showNotice('No Zotero cleanup directories configured in settings.');
return;
}
@ -21,17 +22,17 @@ export const processZoteroCleanup = async (plugin: LatexReferencer, activeView:
.map(d => d.replace(/^\/+|\/+$/g, '')); // remove leading/trailing slashes
if (directories.length === 0) {
new Notice('No valid Zotero cleanup directories configured.');
showNotice('No valid Zotero cleanup directories configured.');
return;
}
const app = plugin.app;
const activeContent = activeView.editor.getValue();
const urlRegex = /zotero:\/\/[^\s\)<>"]+/g;
const urlRegex = /zotero:\/\/[^\s)<>"]+/g;
const activeUrlsMatches = activeContent.match(urlRegex);
if (!activeUrlsMatches || activeUrlsMatches.length === 0) {
new Notice('No Zotero URLs found in the active note.');
showNotice('No Zotero URLs found in the active note.');
return;
}
@ -44,12 +45,12 @@ export const processZoteroCleanup = async (plugin: LatexReferencer, activeView:
});
if (otherFiles.length === 0) {
new Notice('No other markdown files found in the specified directories.');
showNotice('No other Markdown files found in the specified directories.');
return;
}
const existingUrls = new Set<string>();
const notice = new Notice('Scanning directories for duplicate Zotero annotations...', 0);
const notice = showNotice('Scanning directories for duplicate Zotero annotations...', 0);
try {
for (const file of otherFiles) {
@ -66,7 +67,7 @@ export const processZoteroCleanup = async (plugin: LatexReferencer, activeView:
}
if (existingUrls.size === 0) {
new Notice('No duplicate Zotero annotations found.');
showNotice('No duplicate Zotero annotations found.');
return;
}
@ -111,8 +112,8 @@ export const processZoteroCleanup = async (plugin: LatexReferencer, activeView:
const newContent = newLines.join('\n');
if (newContent !== activeContent) {
activeView.editor.setValue(newContent);
new Notice(`Removed ${existingUrls.size} duplicate Zotero annotation(s).`);
showNotice(`Removed ${existingUrls.size} duplicate Zotero annotation(s).`);
} else {
new Notice('No changes made.');
showNotice('No changes made.');
}
};

View file

@ -6,8 +6,8 @@ import {
Menu,
TFolder,
Editor,
Notice,
MarkdownFileInfo
MarkdownFileInfo,
EventRef
} from 'obsidian';
import type { Extension } from '@codemirror/state';
import { around } from 'monkey-around';
@ -33,6 +33,7 @@ import { patchSuggesterWithQuickPreview } from 'ui/quick-preview/patcher';
import { processActiveNoteEquations } from './core/equations/numbering';
import { ExportConfigModal } from './ui/export-pdf/modal';
import { checkAndFixCalloutMath } from 'utils/fixer';
import { showNotice } from 'utils/obsidian';
import { traverseFolder } from 'features/export-pdf/utils';
import { SnippetManager } from 'features/snippets/manager';
import { processZoteroCleanup } from 'features/zotero-cleanup';
@ -44,6 +45,7 @@ import {
} from './features/tikz/row-layout';
import { createTikzLivePreviewPlugin } from './features/tikz/live-preview-overlay';
declare const process: { env: { NODE_ENV?: string } };
const isDev = process.env.NODE_ENV === 'development';
export default class LatexReferencer extends Plugin {
@ -79,7 +81,7 @@ export default class LatexReferencer extends Plugin {
name: 'Remove duplicate Zotero annotations',
editorCallback: (editor: Editor, ctx: MarkdownView | MarkdownFileInfo) => {
if (ctx instanceof MarkdownView) {
processZoteroCleanup(this, ctx);
void processZoteroCleanup(this, ctx);
}
}
});
@ -92,9 +94,9 @@ export default class LatexReferencer extends Plugin {
const fixed = checkAndFixCalloutMath(content);
if (fixed) {
editor.setValue(fixed);
new Notice('Fixed callout equations.');
showNotice('Fixed callout equations.');
} else {
new Notice('No issues found or no changes needed.');
showNotice('No issues found or no changes needed.');
}
}
});
@ -115,7 +117,7 @@ export default class LatexReferencer extends Plugin {
this.addCommand({
id: 'export-current-file-to-pdf',
name: 'Export current file to PDF',
name: 'Export current file to pdf',
checkCallback: (checking: boolean) => {
const view = this.app.workspace.getActiveViewOfType(MarkdownView);
const file = view?.file;
@ -133,7 +135,11 @@ export default class LatexReferencer extends Plugin {
// Menu items for file export
this.registerEvent(
(this.app.workspace as unknown).on('file-menu', (menu: Menu, file: TFile | TFolder) => {
(
this.app.workspace as unknown as {
on(name: 'file-menu', callback: (menu: Menu, file: TFile | TFolder) => void): EventRef;
}
).on('file-menu', (menu: Menu, file: TFile | TFolder) => {
let title = file instanceof TFolder ? 'Export folder to PDF' : 'Better Export PDF';
if (isDev) {
title = `${title} (dev)`;
@ -152,7 +158,11 @@ export default class LatexReferencer extends Plugin {
);
this.registerEvent(
(this.app.workspace as unknown).on('file-menu', (menu: Menu, file: TFile | TFolder) => {
(
this.app.workspace as unknown as {
on(name: 'file-menu', callback: (menu: Menu, file: TFile | TFolder) => void): EventRef;
}
).on('file-menu', (menu: Menu, file: TFile | TFolder) => {
if (file instanceof TFolder) {
let title = 'Export to PDF...';
if (isDev) {
@ -160,11 +170,10 @@ export default class LatexReferencer extends Plugin {
}
menu.addItem(item => {
item.setTitle(title).setIcon('lucide-folder-down').setSection('action');
// @ts-ignore
const subMenu: Menu = item.setSubmenu();
const subMenu = (item as unknown as { setSubmenu: () => Menu }).setSubmenu();
subMenu.addItem(item =>
item
.setTitle('Export each file to PDF')
.setTitle('Export each file to pdf')
.setIcon('lucide-file-stack')
.onClick(async () => {
new ExportConfigModal(this, file, true).open();
@ -206,7 +215,9 @@ export default class LatexReferencer extends Plugin {
this.registerMarkdownPostProcessor(createEquationNumberProcessor(this));
this.registerMarkdownPostProcessor(CustomMathLinksProcessor(this));
this.registerMarkdownPostProcessor(createRowLayoutProcessor(this));
this.app.workspace.onLayoutReady(() => this.forceRerender());
this.app.workspace.onLayoutReady(() => {
void this.forceRerender();
});
this.patchPagePreview();
this.register(setupDOMObserver(this));
@ -217,7 +228,10 @@ export default class LatexReferencer extends Plugin {
}
async loadSettings() {
this.settings = { ...DEFAULT_SETTINGS, ...(await this.loadData()) };
this.settings = {
...DEFAULT_SETTINGS,
...((await this.loadData()) as Partial<PluginSettings>)
};
}
async saveSettings() {
@ -234,34 +248,46 @@ export default class LatexReferencer extends Plugin {
this.app.workspace.updateOptions();
}
forceRerender() {
window.setTimeout(async () => {
for (const leaf of this.app.workspace.getLeavesOfType('markdown')) {
const view = leaf.view as MarkdownView;
private forceRerender() {
this.app.workspace.iterateAllLeaves(leaf => {
const view = leaf.view;
if (view instanceof MarkdownView) {
view.previewMode.rerender(true);
}
}, 800);
});
}
patchPagePreview() {
const pagePreviewPlugin = this.app.internalPlugins.getPluginById('page-preview');
if (!pagePreviewPlugin?.instance) {
console.log('Latex Referencer: Page Preview plugin not found. Cannot patch hover behavior.');
private patchPagePreview() {
const pagePreviewPlugin = this.app.internalPlugins.getPluginById('page-preview') as unknown as {
enabled: boolean;
instance: unknown;
} | null;
if (!pagePreviewPlugin?.enabled) {
return;
}
const instance = pagePreviewPlugin.instance;
const plugin = this;
const app = this.app;
const getEquations = (file: TFile, content: string) =>
processActiveNoteEquations(this, file, content);
const uninstaller = around(instance, {
const uninstaller = around(instance as Record<string, unknown>, {
onLinkHover(old: unknown) {
const oldFunc = old as (
this: unknown,
hoverParent: unknown,
targetEl: unknown,
linktext: string,
sourcePath: string,
state: Record<string, unknown>
) => unknown;
return function (
this: unknown,
hoverParent: unknown,
targetEl: unknown,
linktext: string,
sourcePath: string,
state: unknown
state: Record<string, unknown>
) {
const { path, subpath } = parseLinktext(linktext);
@ -274,33 +300,33 @@ export default class LatexReferencer extends Plugin {
if (subIndexMatch) {
blockId = subpathText.substring(0, subIndexMatch.index);
}
const targetFile = plugin.app.metadataCache.getFirstLinkpathDest(path, sourcePath);
const targetFile = app.metadataCache.getFirstLinkpathDest(path, sourcePath);
if (targetFile instanceof TFile) {
const activeView = plugin.app.workspace.getActiveViewOfType(MarkdownView);
const activeView = app.workspace.getActiveViewOfType(MarkdownView);
const activeFile = activeView?.file;
const activeContent =
typeof activeView?.getViewData === 'function' ? activeView.getViewData() : null;
if (!activeFile || targetFile.path !== activeFile.path || activeContent === null) {
return old.call(this, hoverParent, targetEl, linktext, sourcePath, state);
return oldFunc.call(this, hoverParent, targetEl, linktext, sourcePath, state);
}
const equations = processActiveNoteEquations(plugin, activeFile, activeContent);
const equations = getEquations(activeFile, activeContent);
const targetEquation = equations.get(blockId);
if (targetEquation) {
const line = targetEquation.$position.start;
const newState = { ...state, scroll: line };
// Immediately call the original function with the correct line number
return old.call(this, hoverParent, targetEl, linktext, sourcePath, newState);
return oldFunc.call(this, hoverParent, targetEl, linktext, sourcePath, newState);
}
}
}
// If it's not our link, or if we couldn't find it in the cache,
// call the original function without modification.
return old.call(this, hoverParent, targetEl, linktext, sourcePath, state);
return oldFunc.call(this, hoverParent, targetEl, linktext, sourcePath, state);
};
}
});

View file

@ -2,6 +2,7 @@ import { App, PluginSettingTab, Setting, TextAreaComponent } from 'obsidian';
import LatexReferencer from 'main';
import { NUMBER_STYLES } from './settings';
import { NoteSuggestModal } from '../ui/custom-notes/modal';
import { setCssProps } from 'utils/obsidian';
export class MathSettingTab extends PluginSettingTab {
constructor(
@ -15,7 +16,7 @@ export class MathSettingTab extends PluginSettingTab {
const { containerEl } = this;
containerEl.empty();
new Setting(containerEl).setName('Equation Numbering & Referencing').setHeading();
new Setting(containerEl).setName('Equation numbering & referencing').setHeading();
new Setting(containerEl)
.setName('Number only referenced equations')
@ -79,7 +80,7 @@ export class MathSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName('Show note title in equation link')
.setDesc('If turned on, a link to an equation will be like "Note title > (1.1)".')
.setDesc('If turned on, a link to an equation will be like "note title > (1.1)".')
.addToggle(toggle =>
toggle.setValue(this.plugin.settings.noteTitleInEquationLink).onChange(async value => {
this.plugin.settings.noteTitleInEquationLink = value;
@ -87,7 +88,7 @@ export class MathSettingTab extends PluginSettingTab {
})
);
new Setting(containerEl).setName('Autocomplete & Search').setHeading();
new Setting(containerEl).setName('Autocomplete & search').setHeading();
new Setting(containerEl).setName('Enable autocompletion').addToggle(toggle =>
toggle.setValue(this.plugin.settings.enableSuggest).onChange(async value => {
@ -110,7 +111,7 @@ export class MathSettingTab extends PluginSettingTab {
})
);
new Setting(containerEl).setName('PDF Export').setHeading();
new Setting(containerEl).setName('Pdf export').setHeading();
new Setting(containerEl).setName('Add file name as title').addToggle(toggle =>
toggle
@ -118,7 +119,7 @@ export class MathSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.showTitle)
.onChange(async value => {
this.plugin.settings.showTitle = value;
this.plugin.saveSettings();
await this.plugin.saveSettings();
})
);
new Setting(containerEl).setName('Display headers').addToggle(toggle =>
@ -127,7 +128,7 @@ export class MathSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.displayHeader)
.onChange(async value => {
this.plugin.settings.displayHeader = value;
this.plugin.saveSettings();
await this.plugin.saveSettings();
})
);
new Setting(containerEl).setName('Display footer').addToggle(toggle =>
@ -136,7 +137,7 @@ export class MathSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.displayFooter)
.onChange(async value => {
this.plugin.settings.displayFooter = value;
this.plugin.saveSettings();
await this.plugin.saveSettings();
})
);
@ -146,19 +147,19 @@ export class MathSettingTab extends PluginSettingTab {
.addToggle(toggle =>
toggle.setValue(this.plugin.settings.printBackground).onChange(async value => {
this.plugin.settings.printBackground = value;
this.plugin.saveSettings();
await this.plugin.saveSettings();
})
);
new Setting(containerEl)
.setName('Generate tagged PDF')
.setName('Generate tagged pdf')
.setDesc(
'Whether or not to generate a tagged (accessible) PDF. Defaults to false. As this property is experimental, the generated PDF may not adhere fully to PDF/UA and WCAG standards.'
'Whether or not to generate a tagged (accessible) pdf. Defaults to false. As this property is experimental, the generated pdf may not adhere fully to pdf/ua and wcag standards.'
)
.addToggle(toggle =>
toggle.setValue(this.plugin.settings.generateTaggedPDF).onChange(async value => {
this.plugin.settings.generateTaggedPDF = value;
this.plugin.saveSettings();
await this.plugin.saveSettings();
})
);
@ -170,29 +171,29 @@ export class MathSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.maxLevel)
.onChange(async (value: string) => {
this.plugin.settings.maxLevel = value;
this.plugin.saveSettings();
await this.plugin.saveSettings();
});
});
new Setting(containerEl)
.setName('PDF metadata')
.setName('Pdf metadata')
.setDesc('Add frontMatter(title, author, keywords, subject creator, etc) to pdf metadata')
.addToggle(toggle =>
toggle.setValue(this.plugin.settings.displayMetadata).onChange(async value => {
this.plugin.settings.displayMetadata = value;
this.plugin.saveSettings();
await this.plugin.saveSettings();
})
);
new Setting(containerEl).setName('Advanced').setHeading();
const headerContentAreaSetting = new Setting(containerEl);
headerContentAreaSetting.settingEl.setAttribute(
'style',
'display: grid; grid-template-columns: 1fr;'
);
setCssProps(headerContentAreaSetting.settingEl, {
display: 'grid',
'grid-template-columns': '1fr'
});
headerContentAreaSetting
.setName('Header Template')
.setName('Header template')
.setDesc(
'HTML template for the print header. ' +
'Should be valid HTML markup with following classes used to inject printing values into them: ' +
@ -200,32 +201,36 @@ export class MathSettingTab extends PluginSettingTab {
);
const hederContentArea = new TextAreaComponent(headerContentAreaSetting.controlEl);
setAttributes(hederContentArea.inputEl, {
style: 'margin-top: 12px; width: 100%; height: 6vh;'
setCssProps(hederContentArea.inputEl, {
'margin-top': '12px',
width: '100%',
height: '6vh'
});
hederContentArea.setValue(this.plugin.settings.headerTemplate).onChange(async value => {
this.plugin.settings.headerTemplate = value;
this.plugin.saveSettings();
await this.plugin.saveSettings();
});
const footerContentAreaSetting = new Setting(containerEl);
footerContentAreaSetting.settingEl.setAttribute(
'style',
'display: grid; grid-template-columns: 1fr;'
);
setCssProps(footerContentAreaSetting.settingEl, {
display: 'grid',
'grid-template-columns': '1fr'
});
footerContentAreaSetting
.setName('Footer Template')
.setName('Footer template')
.setDesc(
'HTML template for the print footer. Should use the same format as the headerTemplate.'
'Html template for the print footer. Should use the same format as the headerTemplate.'
);
const footerContentArea = new TextAreaComponent(footerContentAreaSetting.controlEl);
setAttributes(footerContentArea.inputEl, {
style: 'margin-top: 12px; width: 100%; height: 6vh;'
setCssProps(footerContentArea.inputEl, {
'margin-top': '12px',
width: '100%',
height: '6vh'
});
footerContentArea.setValue(this.plugin.settings.footerTemplate).onChange(async value => {
this.plugin.settings.footerTemplate = value;
this.plugin.saveSettings();
await this.plugin.saveSettings();
});
new Setting(containerEl)
@ -259,7 +264,7 @@ export class MathSettingTab extends PluginSettingTab {
});
});
new Setting(containerEl).setName('TikZJax Rendering').setHeading();
new Setting(containerEl).setName('TikZJax rendering').setHeading();
new Setting(containerEl)
.setName('Enable TikZ rendering')
@ -285,12 +290,12 @@ export class MathSettingTab extends PluginSettingTab {
})
);
new Setting(containerEl).setName('Zotero Cleanup').setHeading();
new Setting(containerEl).setName('Zotero cleanup').setHeading();
new Setting(containerEl)
.setName('Directories to search')
.setDesc(
"Comma-separated list of directories to search recursively for Zotero annotations (e.g. 'Zotero,Notes/Readings')."
"Comma-separated list of directories to search recursively for Zotero annotations (e.g. 'Zotero,notes/readings')."
)
.addTextArea(textArea => {
textArea.setValue(this.plugin.settings.zoteroCleanDirectories).onChange(async value => {
@ -300,9 +305,9 @@ export class MathSettingTab extends PluginSettingTab {
textArea.inputEl.setAttr('rows', 3);
});
new Setting(containerEl).setName('Custom Note Hotkeys').setHeading();
new Setting(containerEl).setName('Custom note hotkeys').setHeading();
containerEl.createEl('p', {
text: "Configure hotkeys to quickly open specific notes in your vault. You can define optional default hotkeys here, and further customize or rebind them within Obsidian's global 'Hotkeys' settings.",
text: "Configure hotkeys to quickly open specific notes in your vault. You can define optional default hotkeys here, and further customize or rebind them within Obsidian's global 'hotkeys' settings.",
cls: 'setting-item-description'
});
@ -310,64 +315,68 @@ export class MathSettingTab extends PluginSettingTab {
.setName('Add new hotkey mapping')
.setDesc('Create a new shortcut command to open a specific note.')
.addButton(btn =>
btn
.setButtonText('+ Add hotkey mapping')
.setCta()
.onClick(async () => {
if (!this.plugin.settings.customNoteHotkeys) {
this.plugin.settings.customNoteHotkeys = [];
}
this.plugin.settings.customNoteHotkeys.push({
id: Date.now().toString(),
notePath: '',
name: '',
hotkeyModifiers: ['Mod'],
hotkeyKey: ''
});
await this.plugin.saveSettings();
this.plugin.customNoteManager.registerCommands();
this.display();
})
btn.onClick(async () => {
if (!this.plugin.settings.customNoteHotkeys) {
this.plugin.settings.customNoteHotkeys = [];
}
this.plugin.settings.customNoteHotkeys.push({
id: Date.now().toString(),
notePath: '',
name: '',
hotkeyModifiers: ['Mod'],
hotkeyKey: ''
});
await this.plugin.saveSettings();
this.plugin.customNoteManager.registerCommands();
(this as unknown as { display(): void }).display();
})
);
const hotkeys = this.plugin.settings.customNoteHotkeys || [];
hotkeys.forEach((item, index) => {
const hotkeyContainer = containerEl.createEl('div', {
cls: 'custom-note-hotkey-item',
attr: {
style:
'border: 1px solid var(--background-modifier-border); padding: 15px; margin-bottom: 15px; border-radius: 8px; background-color: var(--background-primary-alt);'
}
cls: 'custom-note-hotkey-item'
});
setCssProps(hotkeyContainer, {
border: '1px solid var(--background-modifier-border)',
padding: '15px',
'margin-bottom': '15px',
'border-radius': '8px',
'background-color': 'var(--background-primary-alt)'
});
const titleRow = hotkeyContainer.createEl('div', {
attr: {
style:
'display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;'
}
const titleRow = hotkeyContainer.createEl('div');
setCssProps(titleRow, {
display: 'flex',
'justify-content': 'space-between',
'align-items': 'center',
'margin-bottom': '10px'
});
titleRow.createEl('strong', { text: `Hotkey Mapping #${index + 1}` });
const deleteBtn = titleRow.createEl('button', {
text: 'Delete',
cls: 'mod-warning',
attr: {
style: 'background-color: var(--background-modifier-error); color: var(--text-on-accent);'
}
cls: 'mod-warning'
});
deleteBtn.addEventListener('click', async () => {
hotkeys.splice(index, 1);
await this.plugin.saveSettings();
this.plugin.customNoteManager.registerCommands();
this.display();
setCssProps(deleteBtn, {
'background-color': 'var(--background-modifier-error)',
color: 'var(--text-on-accent)'
});
deleteBtn.addEventListener('click', () => {
void (async () => {
hotkeys.splice(index, 1);
await this.plugin.saveSettings();
this.plugin.customNoteManager.registerCommands();
(this as unknown as { display(): void }).display();
})();
});
new Setting(hotkeyContainer)
.setName('Friendly name')
.setDesc("A clear label for the Obsidian command (e.g. 'Daily Planner').")
.setDesc("A clear label for the Obsidian command (e.g. 'daily planner').")
.addText(text =>
text
.setPlaceholder('e.g. My Note')
.setPlaceholder('E.g. My note')
.setValue(item.name)
.onChange(async value => {
item.name = value;
@ -378,7 +387,7 @@ export class MathSettingTab extends PluginSettingTab {
const pathSetting = new Setting(hotkeyContainer)
.setName('Note path')
.setDesc('The relative vault path to the target markdown note.');
.setDesc('The relative vault path to the target Markdown note.');
pathSetting.addText(text => {
text
@ -393,13 +402,15 @@ export class MathSettingTab extends PluginSettingTab {
pathSetting.addButton(btn =>
btn
.setIcon('search')
.setTooltip('Browse/Search vault notes')
.setTooltip('Browse/search vault notes')
.onClick(() => {
new NoteSuggestModal(this.app, async file => {
text.setValue(file.path);
item.notePath = file.path;
await this.plugin.saveSettings();
this.plugin.customNoteManager.registerCommands();
new NoteSuggestModal(this.app, file => {
void (async () => {
text.setValue(file.path);
item.notePath = file.path;
await this.plugin.saveSettings();
this.plugin.customNoteManager.registerCommands();
})();
}).open();
})
);
@ -415,7 +426,7 @@ export class MathSettingTab extends PluginSettingTab {
hotkeySetting.addToggle(toggle =>
toggle
.setTooltip('Ctrl / Cmd (Mod)')
.setTooltip('Ctrl / cmd (mod)')
.setValue(isMod)
.onChange(async value => {
if (value) {
@ -427,10 +438,10 @@ export class MathSettingTab extends PluginSettingTab {
this.plugin.customNoteManager.registerCommands();
})
);
hotkeySetting.controlEl.createSpan({
text: 'Ctrl/Cmd ',
attr: { style: 'margin-right: 15px; font-size: 0.9em;' }
const span1 = hotkeySetting.controlEl.createSpan({
text: 'Ctrl/Cmd '
});
setCssProps(span1, { 'margin-right': '15px', 'font-size': '0.9em' });
hotkeySetting.addToggle(toggle =>
toggle
@ -446,10 +457,10 @@ export class MathSettingTab extends PluginSettingTab {
this.plugin.customNoteManager.registerCommands();
})
);
hotkeySetting.controlEl.createSpan({
text: 'Alt ',
attr: { style: 'margin-right: 15px; font-size: 0.9em;' }
const span2 = hotkeySetting.controlEl.createSpan({
text: 'Alt '
});
setCssProps(span2, { 'margin-right': '15px', 'font-size': '0.9em' });
hotkeySetting.addToggle(toggle =>
toggle
@ -465,10 +476,10 @@ export class MathSettingTab extends PluginSettingTab {
this.plugin.customNoteManager.registerCommands();
})
);
hotkeySetting.controlEl.createSpan({
text: 'Shift ',
attr: { style: 'margin-right: 15px; font-size: 0.9em;' }
const span3 = hotkeySetting.controlEl.createSpan({
text: 'Shift '
});
setCssProps(span3, { 'margin-right': '15px', 'font-size': '0.9em' });
hotkeySetting.addText(text =>
text
@ -494,9 +505,3 @@ export class MathSettingTab extends PluginSettingTab {
});
}
}
function setAttributes(element: HTMLTextAreaElement, attributes: { [x: string]: string }) {
for (const key in attributes) {
element.setAttribute(key, attributes[key]);
}
}

View file

@ -47,7 +47,7 @@
.math-booster-begin-proof {
padding-right: 10px;
font-family: CMU Serif, Times, Noto Serif JP;
font-family: CMU Serif, Times, Noto Serif JP, serif;
font-weight: bold;
}
@ -132,7 +132,7 @@
background-color: rgb(0, 0, 0, 0);
border: solid var(--border-width);
border-radius: var(--size-2-3);
font-family: CMU Serif, Times, Noto Serif JP;
font-family: CMU Serif, Times, Noto Serif JP, serif;
}
.theme-framed .theorem-callout .callout-icon {
@ -140,7 +140,7 @@
}
.theme-framed .theorem-callout-main-title {
font-family: CMU Serif, Times, Noto Sans JP;
font-family: CMU Serif, Times, Noto Sans JP, sans-serif;
font-weight: bolder;
}
@ -162,7 +162,7 @@
padding-right: 0;
border: none;
box-shadow: none;
font-family: CMU Serif, Times, Noto Serif JP;
font-family: CMU Serif, Times, Noto Serif JP, serif;
}
.theme-plain .theorem-callout .callout-icon {
@ -170,7 +170,7 @@
}
.theme-plain .theorem-callout-main-title {
font-family: CMU Serif, Times, Noto Sans JP;
font-family: CMU Serif, Times, Noto Sans JP, sans-serif;
font-weight: bolder;
}
@ -194,7 +194,7 @@
border-radius: 0px;
box-shadow: none;
padding: 0px;
font-family: CMU Serif, Times, Noto Serif JP;
font-family: CMU Serif, Times, Noto Serif JP, serif;
}
.theme-vivid .theorem-callout .callout-title {
@ -207,7 +207,7 @@
}
.theme-vivid .theorem-callout .callout-title-inner {
font-family: Inter;
font-family: Inter, sans-serif;
font-weight: normal;
color: rgb(var(--callout-color));
}
@ -226,7 +226,7 @@
.theme-mathwiki .theorem-callout {
--callout-color: 248, 248, 255;
font-family: CMU Serif, Times, Noto Serif JP;
font-family: CMU Serif, Times, Noto Serif JP, serif;
}
.theme-mathwiki .theorem-callout .callout-title-inner {

View file

@ -1,5 +1,5 @@
import * as fs from 'fs/promises';
import {
App,
ButtonComponent,
type FrontMatterCache,
Modal,
@ -8,10 +8,36 @@ import {
TFolder,
debounce
} from 'obsidian';
import * as path from 'path';
import { PageSize } from 'features/export-pdf/constant';
import LatexReferencer from 'main';
import { exportToPDF, getOutputFile, getOutputPath } from 'features/export-pdf/pdf';
import { showNotice, setCssProps } from 'utils/obsidian';
interface FsPromisesModule {
readFile(path: string, options: { encoding: string }): Promise<string>;
}
interface PathModule {
join(...paths: string[]): string;
}
interface ElectronWebView extends HTMLIFrameElement {
executeJavaScript(script: string): Promise<[number, number]>;
insertCSS(css: string): Promise<void>;
openDevTools(): void;
printToPDF(options: Record<string, unknown>): Promise<ArrayBuffer>;
}
const getRequire = (): ((id: string) => unknown) | null => {
if (typeof window !== 'undefined' && 'require' in window) {
return (window as unknown as { require: (id: string) => unknown }).require;
}
return null;
};
const req = getRequire();
const fs = req ? (req('fs/promises') as FsPromisesModule) : null;
const path = req ? (req('path') as PathModule) : null;
import {
createWebview,
fixDoc,
@ -61,11 +87,28 @@ export type DocType = { doc: Document; frontMatter?: FrontMatterCache; file: TFi
type Callback = (conf: TConfig) => void;
function fullWidthButton(button: ButtonComponent) {
button.buttonEl.setAttribute('style', `margin: "0 auto"; width: -webkit-fill-available`);
setCssProps(button.buttonEl, {
margin: '0 auto',
width: '-webkit-fill-available'
});
}
function setInputWidth(inputEl: HTMLInputElement) {
inputEl.setAttribute('style', `width: 100px;`);
setCssProps(inputEl, {
width: '100px'
});
}
interface CustomCssApp {
customCss?: {
snippets?: string[];
enabledSnippets?: Set<string>;
};
vault: App['vault'] & {
adapter: {
basePath: string;
};
};
}
export class ExportConfigModal extends Modal {
@ -74,16 +117,19 @@ export class ExportConfigModal extends Modal {
multiplePdf?: boolean;
callback!: Callback;
file: TFile | TFolder;
preview: unknown;
webviews: unknown[];
preview: ElectronWebView | null = null;
webviews: ElectronWebView[];
previewDiv!: HTMLDivElement;
completed: boolean;
docs: DocType[];
title!: string;
frontMatter!: FrontMatterCache;
scale: number;
scale!: number;
// @ts-ignore
svelte: Progress;
svelte: {
initRenderStates(data: ParamType[]): void;
updateRenderStates(i: number): void;
} | null = null;
constructor(
public plugin: LatexReferencer,
@ -182,23 +228,30 @@ export class ExportConfigModal extends Modal {
});
}
parseToc(doc: Document) {
const cache = this.getFileCache(this.file as TFile);
const files =
if (!(this.file instanceof TFile)) return [];
const cache = this.getFileCache(this.file);
const results =
cache?.links
?.map(({ link, displayText }) => {
const id = crypto.randomUUID();
const id: string = crypto.randomUUID();
const elem = doc.querySelector(`a[data-href="${link}"]`) as HTMLAnchorElement;
if (elem) {
elem.href = `#${id}`;
}
const target = this.plugin.app.metadataCache.getFirstLinkpathDest(link, this.file.path);
if (!(target instanceof TFile)) {
return null;
}
return {
title: displayText,
file: this.app.metadataCache.getFirstLinkpathDest(link, this.file.path) as TFile,
file: target,
id
};
})
.filter(item => item.file instanceof TFile) ?? [];
return files;
.filter(
(item): item is { title: string | undefined; file: TFile; id: string } => item !== null
) ?? [];
return results;
}
mergeDoc(docs: DocType[]) {
@ -240,9 +293,11 @@ export class ExportConfigModal extends Modal {
const scale = Math.floor((mm2px(width) / el.offsetWidth) * 100) / 100;
this.webviews.forEach(wb => {
wb.style.transform = `scale(${1 / scale},${1 / scale})`;
wb.style.width = `calc(${scale} * 100%)`;
wb.style.height = `${mm2px(h)}px`;
setCssProps(wb, {
transform: `scale(${1 / scale},${1 / scale})`,
width: `calc(${scale} * 100%)`,
height: `${mm2px(h)}px`
});
});
this.scale = scale;
return scale;
@ -250,24 +305,27 @@ export class ExportConfigModal extends Modal {
async calcWebviewSize() {
await sleep(500);
this.webviews.forEach(async (e, i) => {
for (let i = 0; i < this.webviews.length; i++) {
const e = this.webviews[i];
const [width, height] = await e.executeJavaScript(
'[document.body.offsetWidth, document.body.offsetHeight]'
);
const sizeEl = e.parentNode?.querySelector('.print-size');
if (sizeEl) {
sizeEl.innerHTML = `${width}×${height}px\n${px2mm(width)}×${px2mm(height)}mm`;
sizeEl.empty();
sizeEl.createDiv({ text: `${width}×${height}px` });
sizeEl.createDiv({ text: `${px2mm(width)}×${px2mm(height)}mm` });
}
});
}
}
async togglePrintSize() {
document.querySelectorAll('.print-size')?.forEach((el: Element) => {
activeDocument.querySelectorAll('.print-size')?.forEach((el: Element) => {
const sizeEl = el as HTMLDivElement;
if (this.config['pageSize'] == 'Custom') {
sizeEl.style.visibility = 'visible';
if (this.config['pageSize'] === 'Custom') {
setCssProps(sizeEl, { visibility: 'visible' });
} else {
sizeEl.style.visibility = 'hidden';
setCssProps(sizeEl, { visibility: 'hidden' });
}
});
}
@ -275,7 +333,7 @@ export class ExportConfigModal extends Modal {
makeWebviewJs(doc: Document) {
return `
document.body.innerHTML = decodeURIComponent(\`${encodeURIComponent(doc.body.innerHTML)}\`);
document.head.innerHTML = decodeURIComponent(\`${encodeURIComponent(document.head.innerHTML)}\`);
document.head.innerHTML = decodeURIComponent(\`${encodeURIComponent(activeDocument.head.innerHTML)}\`);
// Function to recursively decode and replace innerHTML of span.markdown-embed elements
function decodeAndReplaceEmbed(element) {
@ -288,9 +346,9 @@ export class ExportConfigModal extends Modal {
// Start the process with all span.markdown-embed elements in the document
document.querySelectorAll("span.markdown-embed").forEach(decodeAndReplaceEmbed);
document.body.setAttribute("class", \`${document.body.getAttribute('class')}\`)
document.body.setAttribute("style", \`${document.body.getAttribute('style')}\`)
document.body.setAttribute("class", \`${activeDocument.body.getAttribute('class')}\`)
document.body.setAttribute("style", \`${activeDocument.body.getAttribute('style')}\`)
document.body.addClass("theme-light");
document.body.removeClass("theme-dark");
document.title = \`${doc.title}\`;
@ -302,30 +360,36 @@ export class ExportConfigModal extends Modal {
* @param render Rerender or not
*/
async appendWebview(e: HTMLDivElement, doc: Document) {
const webview = createWebview(this.scale);
const webview = createWebview(this.scale) as unknown as ElectronWebView;
const preview = e.appendChild(webview);
this.webviews.push(preview);
this.preview = preview;
preview.addEventListener('dom-ready', async e => {
this.completed = true;
getAllStyles().forEach(async css => {
await preview.insertCSS(css);
});
if (this.config.cssSnippet && this.config.cssSnippet != '0') {
try {
const cssSnippet = await fs.readFile(this.config.cssSnippet, { encoding: 'utf8' });
// remove `@media print { ... }`
const printCss = cssSnippet.replaceAll(/@media print\s*{([^}]+)}/g, '$1');
await preview.insertCSS(printCss);
await preview.insertCSS(cssSnippet);
} catch (error) {
console.warn(error);
preview.addEventListener('dom-ready', () => {
void (async () => {
this.completed = true;
const styles = getAllStyles();
for (const css of styles) {
await preview.insertCSS(css);
}
}
await preview.executeJavaScript(this.makeWebviewJs(doc));
getPatchStyle().forEach(async css => {
await preview.insertCSS(css);
});
if (this.config.cssSnippet && this.config.cssSnippet !== '0') {
try {
if (fs) {
const cssSnippet = await fs.readFile(this.config.cssSnippet, { encoding: 'utf8' });
// remove `@media print { ... }`
const printCss = cssSnippet.replaceAll(/@media print\s*{([^}]+)}/g, '$1');
await preview.insertCSS(printCss);
await preview.insertCSS(cssSnippet);
}
} catch {
// Ignore snippet loading issues
}
}
await preview.executeJavaScript(this.makeWebviewJs(doc));
const patchStyles = getPatchStyle();
for (const css of patchStyles) {
await preview.insertCSS(css);
}
})();
});
}
async appendWebviews(el: HTMLDivElement, render = true) {
@ -340,8 +404,10 @@ export class ExportConfigModal extends Modal {
}
});
const { data, docs } = await this.getAllFiles();
this.svelte.initRenderStates(data);
await this.renderFiles(data, docs, this.svelte.updateRenderStates);
if (this.svelte) {
this.svelte.initRenderStates(data);
await this.renderFiles(data, docs, i => this.svelte?.updateRenderStates(i));
}
}
el.empty();
await Promise.all(
@ -362,29 +428,33 @@ export class ExportConfigModal extends Modal {
async onOpen() {
this.contentEl.empty();
this.modalEl.addClass('better-export-pdf-modal');
this.containerEl.style.setProperty('--dialog-width', '90vw');
this.containerEl.style.setProperty('--dialog-height', '90vh');
setCssProps(this.containerEl, {
'--dialog-width': '90vw',
'--dialog-height': '90vh'
});
this.titleEl.setText('Export to PDF');
this.titleEl.setText('Export to pdf');
const wrapper = this.contentEl.createDiv({ attr: { id: 'better-export-pdf' } });
const title = (this.file as TFile)?.basename ?? this.file?.name;
const title = this.file instanceof TFile ? this.file.basename : this.file.name;
this.previewDiv = wrapper.createDiv({ attr: { class: 'pdf-preview' } }, async el => {
this.previewDiv = wrapper.createDiv({ attr: { class: 'pdf-preview' } }, el => {
el.empty();
const resizeObserver = new ResizeObserver(() => {
this.calcPageSize(el);
});
resizeObserver.observe(el);
await this.appendWebviews(el);
this.calcPageSize(el);
this.togglePrintSize();
void (async () => {
await this.appendWebviews(el);
this.calcPageSize(el);
await this.togglePrintSize();
})();
});
const contentEl = wrapper.createDiv({ attr: { class: 'setting-wrapper' } });
contentEl.addEventListener('keyup', event => {
if (event.key === 'Enter') {
handleExport();
void handleExport();
}
});
this.generateForm(contentEl);
@ -392,19 +462,18 @@ export class ExportConfigModal extends Modal {
const handleExport = async () => {
this.plugin.settings.prevConfig = this.config;
await this.plugin.saveSettings();
if (this.config['pageSize'] == 'Custom') {
if (this.config['pageSize'] === 'Custom') {
if (
!isNumber(this.config['pageWidth'] ?? '') ||
!isNumber(this.config['pageHeight'] ?? '')
) {
alert('When the page size is Custom, the Width/Height cannot be empty.');
showNotice('When the page size is Custom, the Width/Height cannot be empty.');
return;
}
}
if (this.multiplePdf) {
const outputPath = await getOutputPath(title);
console.log('output:', outputPath);
if (outputPath) {
await Promise.all(
this.webviews.map(async (wb, i) => {
@ -433,21 +502,27 @@ export class ExportConfigModal extends Modal {
};
new Setting(contentEl).setHeading().addButton(button => {
button.setButtonText('Export').onClick(handleExport);
button.setButtonText('Export').onClick(() => {
void handleExport();
});
button.setCta();
fullWidthButton(button);
});
new Setting(contentEl).setHeading().addButton(button => {
button.setButtonText('Refresh').onClick(async () => {
await this.appendWebviews(this.previewDiv);
button.setButtonText('Refresh').onClick(() => {
void (async () => {
await this.appendWebviews(this.previewDiv);
})();
});
fullWidthButton(button);
});
const debugEl = new Setting(contentEl).setHeading().addButton(button => {
button.setButtonText('Debug').onClick(async () => {
this.preview?.openDevTools();
button.setButtonText('Debug').onClick(() => {
void (async () => {
this.preview?.openDevTools();
})();
});
fullWidthButton(button);
});
@ -462,7 +537,7 @@ export class ExportConfigModal extends Modal {
.onChange(async value => {
this.config['showTitle'] = value;
this.webviews.forEach((wv, i) => {
wv.executeJavaScript(`
void wv.executeJavaScript(`
var _title = document.querySelector("h1.__title__");
if (_title) {
_title.style.display = "${value ? 'block' : 'none'}";
@ -470,7 +545,7 @@ export class ExportConfigModal extends Modal {
`);
const _title = this.docs[i]?.doc?.querySelector('h1.__title__') as HTMLHeadingElement;
if (_title) {
_title.style.display = value ? 'block' : 'none';
setCssProps(_title, { display: value ? 'block' : 'none' });
}
});
})
@ -489,36 +564,40 @@ export class ExportConfigModal extends Modal {
'Ledger',
'Custom'
];
new Setting(contentEl).setName('Page Size').addDropdown(dropdown => {
new Setting(contentEl).setName('Page size').addDropdown(dropdown => {
dropdown
.addOptions(Object.fromEntries(pageSizes.map(size => [size, size])))
.setValue(this.config.pageSize)
.onChange(async (value: string) => {
this.config['pageSize'] = value;
if (value == 'Custom') {
sizeEl.settingEl.hidden = false;
} else {
sizeEl.settingEl.hidden = true;
}
this.togglePrintSize();
this.calcPageSize();
await this.calcWebviewSize();
.onChange((value: string) => {
void (async () => {
this.config['pageSize'] = value;
if (value === 'Custom') {
sizeEl.settingEl.hidden = false;
} else {
sizeEl.settingEl.hidden = true;
}
void this.togglePrintSize();
this.calcPageSize();
await this.calcWebviewSize();
})();
});
});
const sizeEl = new Setting(contentEl)
.setName('Width/Height')
.setName('Width/height')
.addText(text => {
setInputWidth(text.inputEl);
text
.setPlaceholder('width')
.setPlaceholder('Width')
.setValue(this.config['pageWidth'] as string)
.onChange(
debounce(
async value => {
this.config['pageWidth'] = value;
this.calcPageSize();
await this.calcWebviewSize();
value => {
void (async () => {
this.config['pageWidth'] = value;
this.calcPageSize();
await this.calcWebviewSize();
})();
},
500,
true
@ -528,7 +607,7 @@ export class ExportConfigModal extends Modal {
.addText(text => {
setInputWidth(text.inputEl);
text
.setPlaceholder('height')
.setPlaceholder('Height')
.setValue(this.config['pageHeight'] as string)
.onChange(value => {
this.config['pageHeight'] = value;
@ -547,9 +626,9 @@ export class ExportConfigModal extends Modal {
.addOption('2', 'Small')
.addOption('3', 'Custom')
.setValue(this.config['marginType'])
.onChange(async (value: string) => {
.onChange((value: string) => {
this.config['marginType'] = value;
if (value == '3') {
if (value === '3') {
topEl.settingEl.hidden = false;
btmEl.settingEl.hidden = false;
} else {
@ -560,11 +639,11 @@ export class ExportConfigModal extends Modal {
});
const topEl = new Setting(contentEl)
.setName('Top/Bottom')
.setName('Top/bottom')
.addText(text => {
setInputWidth(text.inputEl);
text
.setPlaceholder('margin top')
.setPlaceholder('Margin top')
.setValue(this.config['marginTop'] as string)
.onChange(value => {
this.config['marginTop'] = value;
@ -573,19 +652,19 @@ export class ExportConfigModal extends Modal {
.addText(text => {
setInputWidth(text.inputEl);
text
.setPlaceholder('margin bottom')
.setPlaceholder('Margin bottom')
.setValue(this.config['marginBottom'] as string)
.onChange(value => {
this.config['marginBottom'] = value;
});
});
topEl.settingEl.hidden = this.config['marginType'] != '3';
topEl.settingEl.hidden = this.config['marginType'] !== '3';
const btmEl = new Setting(contentEl)
.setName('Left/Right')
.setName('Left/right')
.addText(text => {
setInputWidth(text.inputEl);
text
.setPlaceholder('margin left')
.setPlaceholder('Margin left')
.setValue(this.config['marginLeft'] as string)
.onChange(value => {
this.config['marginLeft'] = value;
@ -594,15 +673,15 @@ export class ExportConfigModal extends Modal {
.addText(text => {
setInputWidth(text.inputEl);
text
.setPlaceholder('margin right')
.setPlaceholder('Margin right')
.setValue(this.config['marginRight'] as string)
.onChange(value => {
this.config['marginRight'] = value;
});
});
btmEl.settingEl.hidden = this.config['marginType'] != '3';
btmEl.settingEl.hidden = this.config['marginType'] !== '3';
new Setting(contentEl).setName('Downscale Percent').addSlider(slider => {
new Setting(contentEl).setName('Downscale percent').addSlider(slider => {
slider
.setLimits(0, 100, 1)
.setValue(this.config['scale'])
@ -613,14 +692,14 @@ export class ExportConfigModal extends Modal {
});
new Setting(contentEl).setName('Landscape').addToggle(toggle =>
toggle
.setTooltip('landscape')
.setTooltip('Landscape')
.setValue(this.config['landscape'])
.onChange(async value => {
this.config['landscape'] = value;
})
);
new Setting(contentEl).setName('Display Header').addToggle(toggle =>
new Setting(contentEl).setName('Display header').addToggle(toggle =>
toggle
.setTooltip('Display header')
.setValue(this.config['displayHeader'])
@ -629,7 +708,7 @@ export class ExportConfigModal extends Modal {
})
);
new Setting(contentEl).setName('Display Footer').addToggle(toggle =>
new Setting(contentEl).setName('Display footer').addToggle(toggle =>
toggle
.setTooltip('Display footer')
.setValue(this.config['displayFooter'])
@ -650,14 +729,16 @@ export class ExportConfigModal extends Modal {
const snippets = this.cssSnippets();
if (Object.keys(snippets).length > 0 && this.plugin.settings.enabledCss) {
new Setting(contentEl).setName('CSS snippets').addDropdown(dropdown => {
new Setting(contentEl).setName('Css snippets').addDropdown(dropdown => {
dropdown
.addOption('0', 'Not select')
.addOptions(snippets)
.setValue(this.config['cssSnippet'] as string)
.onChange(async (value: string) => {
this.config['cssSnippet'] = value;
await this.appendWebviews(this.previewDiv, false);
.onChange((value: string) => {
void (async () => {
this.config['cssSnippet'] = value;
await this.appendWebviews(this.previewDiv, false);
})();
});
});
}
@ -668,22 +749,21 @@ export class ExportConfigModal extends Modal {
contentEl.empty();
if (this.svelte) {
// Remove the Counter from the ItemView.
unmount(this.svelte);
void unmount(this.svelte);
}
}
cssSnippets(): Record<string, string> {
// @ts-ignore
const { snippets, enabledSnippets } = this.app?.customCss ?? {};
// @ts-ignore
const basePath = this.app.vault.adapter.basePath;
return Object.fromEntries(
snippets
?.filter((item: string) => !enabledSnippets.has(item))
.map((name: string) => {
const file = path.join(basePath, this.app.vault.configDir, 'snippets', `${name}.css`);
return [file, name];
})
);
const customApp = this.app as unknown as CustomCssApp;
const { snippets, enabledSnippets } = customApp.customCss ?? {};
const basePath = customApp.vault.adapter.basePath;
if (!path || !snippets || !enabledSnippets) return {};
const entries = snippets
.filter((item: string) => !enabledSnippets.has(item))
.map((name: string): [string, string] => {
const file = path.join(basePath, this.app.vault.configDir, 'snippets', `${name}.css`);
return [file, name];
});
return Object.fromEntries(entries);
}
}

View file

@ -5,22 +5,24 @@ import { PatchedSuggester, PreviewInfo, Suggester } from './types';
export function patchSuggesterWithQuickPreview<T>(
plugin: LatexReferencer,
suggesterClass: new (...args: unknown[]) => Suggester<T>,
suggesterClass: new (plugin: LatexReferencer) => Suggester<T>,
itemNormalizer: (item: T) => PreviewInfo | null
) {
const uninstaller = around(suggesterClass.prototype, {
open(old) {
open(old: unknown) {
const oldFunc = old as (this: unknown) => void;
return function (this: PatchedSuggester<T>) {
old.call(this);
oldFunc.call(this);
if (!this.popoverManager) {
this.popoverManager = new PopoverManager<T>(plugin, this, itemNormalizer);
}
this.popoverManager.load();
};
},
close(old) {
close(old: unknown) {
const oldFunc = old as (this: unknown) => void;
return function (this: PatchedSuggester<T>) {
old.call(this);
oldFunc.call(this);
// close() can be called before open() at startup, so we need the optional chaining (?.)
this.popoverManager?.unload();
};

View file

@ -4,7 +4,6 @@ import {
KeymapEventHandler,
PopoverSuggest,
SuggestModal,
UserEvent,
Suggestions
} from 'obsidian';
@ -105,12 +104,20 @@ export class PopoverManager<T> extends Component {
const info = this.itemNormalizer(item);
if (info) {
// @ts-ignore - Using internal API
const self = this.plugin.app.internalPlugins.getPluginById('page-preview').instance;
// @ts-ignore - Using internal API
self.onLinkHover(this.currentHoverParent, this.doc.body, info.linktext, info.sourcePath, {
scroll: info.line
});
const self = this.plugin.app.internalPlugins.getPluginById('page-preview')?.instance as {
onLinkHover(
parent: unknown,
body: HTMLElement,
linktext: string,
sourcePath: string,
state: { scroll?: number }
): void;
} | null;
if (self) {
self.onLinkHover(this.currentHoverParent, this.doc.body, info.linktext, info.sourcePath, {
scroll: info.line
});
}
}
}

View file

@ -1,4 +1,4 @@
import { PopoverSuggest, SuggestModal } from 'obsidian';
import { PopoverSuggest, SuggestModal, Suggestions, HoverParent } from 'obsidian';
import { PopoverManager } from './popoverManager';
export type Suggester<T> = PopoverSuggest<T> | SuggestModal<T>;
@ -20,6 +20,7 @@ declare module 'obsidian' {
interface SuggestModal<T> {
isOpen: boolean;
chooser: Suggestions<T>;
}
interface HoverPopover {

View file

@ -3,7 +3,6 @@ import {
Editor,
EditorSuggestContext,
Instruction,
Notice,
Scope,
SearchResult,
TFile,
@ -13,6 +12,7 @@ import {
renderMath,
sortSearchResults
} from 'obsidian';
import { showNotice } from 'utils/obsidian';
import LatexReferencer from 'main';
import { EquationBlock } from 'types';
import { LEAF_OPTION_TO_ARGS } from '../../settings/settings';
@ -51,7 +51,7 @@ export class ActiveNoteSearchCore {
const item = this.parent.getSelectedItem();
const file = this.app.vault.getAbstractFileByPath(item.$file);
if (!(file instanceof TFile)) return;
openFileAndSelectPosition(
void openFileAndSelectPosition(
this.app,
file,
item.$pos,
@ -86,7 +86,7 @@ export class ActiveNoteSearchCore {
async getSuggestions(query: string): Promise<EquationBlock[]> {
const blocks = await this.getUnsortedSuggestions();
const callback = (
this.plugin.settings.searchMethod == 'Fuzzy' ? prepareFuzzySearch : prepareSimpleSearch
this.plugin.settings.searchMethod === 'Fuzzy' ? prepareFuzzySearch : prepareSimpleSearch
)(query);
const results: ScoredEquationBlock[] = [];
@ -117,8 +117,8 @@ export class ActiveNoteSearchCore {
}
selectSuggestion(item: EquationBlock, evt: MouseEvent | KeyboardEvent): void {
this.selectSuggestionImpl(item);
finishRenderMath();
void this.selectSuggestionImpl(item);
void finishRenderMath();
}
async selectSuggestionImpl(block: EquationBlock): Promise<void> {
@ -140,7 +140,7 @@ export class ActiveNoteSearchCore {
{ line: end.line + lineAdded, ch: end.ch }
);
} else {
new Notice(`${this.plugin.manifest.name}: Failed to read cache. Retry again later.`, 5000);
showNotice(`${this.plugin.manifest.name}: Failed to read cache. Retry again later.`, 5000);
}
}
}

View file

@ -1,9 +1,10 @@
import { App, Editor, FuzzyMatch, FuzzySuggestModal, Notice } from 'obsidian';
import { App, Editor, FuzzyMatch, FuzzySuggestModal } from 'obsidian';
import {
BUILTIN_TEXT_TRANSFORM_SNIPPETS,
runTextTransformSnippet,
TextTransformSnippet
} from '../../features/snippets/transforms';
import { showNotice } from 'utils/obsidian';
export class TextTransformSuggestModal extends FuzzySuggestModal<TextTransformSnippet> {
constructor(
@ -30,9 +31,9 @@ export class TextTransformSuggestModal extends FuzzySuggestModal<TextTransformSn
onChooseItem(item: TextTransformSnippet, evt: MouseEvent | KeyboardEvent): void {
const result = runTextTransformSnippet(this.editor, item);
if (result.changedCount > 0) {
new Notice(`Applied ${item.name} to ${result.changedCount} ${result.appliedOn}(s).`);
showNotice(`Applied ${item.name} to ${result.changedCount} ${result.appliedOn}(s).`);
return;
}
new Notice(`${item.name} made no changes to the current ${result.appliedOn}.`);
showNotice(`${item.name} made no changes to the current ${result.appliedOn}.`);
}
}

View file

@ -29,9 +29,9 @@ function writeDebugLine(line: string, plugin?: DebugCapablePlugin) {
try {
const adapter = plugin?.app?.vault?.adapter;
if (adapter && typeof adapter.append === 'function') {
adapter.append('latex-referencer-debug.log', `${line}\n`);
void adapter.append('latex-referencer-debug.log', `${line}\n`);
}
} catch (_) {
} catch {
// Best-effort only. Console logging should still work.
}
}
@ -64,9 +64,9 @@ export function clearDebugLog(plugin?: DebugCapablePlugin) {
try {
const adapter = plugin?.app?.vault?.adapter;
if (adapter && typeof adapter.write === 'function') {
adapter.write('latex-referencer-debug.log', '');
void adapter.write('latex-referencer-debug.log', '');
}
} catch (_) {
} catch {
// Ignore write failures.
}
}

View file

@ -69,7 +69,7 @@ export class NonActiveNoteIO extends FileIO {
}
async setLine(lineNumber: number, text: string): Promise<void> {
this.plugin.app.vault.process(this.file, (data: string): string => {
await this.plugin.app.vault.process(this.file, (data: string): string => {
const lines = splitIntoLines(data);
lines[lineNumber] = text;
return lines.join('\n');
@ -77,7 +77,7 @@ export class NonActiveNoteIO extends FileIO {
}
async setRange(position: Pos, text: string): Promise<void> {
this.plugin.app.vault.process(this.file, (data: string): string => {
await this.plugin.app.vault.process(this.file, (data: string): string => {
return (
data.slice(0, position.start.offset) +
text +
@ -87,7 +87,7 @@ export class NonActiveNoteIO extends FileIO {
}
async insertLine(lineNumber: number, text: string): Promise<void> {
this.plugin.app.vault.process(this.file, (data: string): string => {
await this.plugin.app.vault.process(this.file, (data: string): string => {
const lines = splitIntoLines(data);
insertAt(lines, text, lineNumber);
return lines.join('\n');
@ -116,7 +116,7 @@ export function getIO(
activeMarkdownView?: MarkdownView | null
) {
activeMarkdownView = activeMarkdownView ?? plugin.app.workspace.getActiveViewOfType(MarkdownView);
if (activeMarkdownView && activeMarkdownView.file == file && isEditingView(activeMarkdownView)) {
if (activeMarkdownView && activeMarkdownView.file === file && isEditingView(activeMarkdownView)) {
return new ActiveNoteIO(plugin, file, activeMarkdownView.editor);
}
return new NonActiveNoteIO(plugin, file);

View file

@ -58,7 +58,13 @@ export function generateEqId(length: number = 8): string {
return result;
}
export function showNotice(message: string): Notice {
export function showNotice(message: string, duration?: number): Notice {
const NoticeConstructor = Notice;
return new NoticeConstructor(message);
return new NoticeConstructor(message, duration);
}
export function setCssProps(el: HTMLElement, props: Record<string, string>) {
for (const [key, value] of Object.entries(props)) {
el.style.setProperty(key, value);
}
}

View file

@ -1,9 +1,9 @@
import LatexReferencer from 'main';
import { Editor, TFile, CachedMetadata, Notice } from 'obsidian';
import LatexReferencer from '../main';
import { Editor, TFile, CachedMetadata } from 'obsidian';
import { getIO } from './file-io';
import { getCalloutPrefix, isStructuralCalloutLine } from './parse';
import { EquationBlock } from 'types';
import { generateEqId } from './obsidian';
import { generateEqId, showNotice } from './obsidian';
export function insertDisplayMath(editor: Editor) {
const cursorPos = editor.getCursor();
@ -41,7 +41,7 @@ export async function insertBlockIdIfNotExist(
const insertOffsetInBlock = originalText.lastIndexOf('$$');
if (insertOffsetInBlock === -1) {
// This should not be reached if the block is a valid math block.
new Notice(`${plugin.manifest.name}: Could not find closing $$ in the math block.`);
showNotice(`${plugin.manifest.name}: Could not find closing $$ in the math block.`);
return;
}

View file

@ -57,7 +57,8 @@ describe('format.ts tests', () => {
it('getEqNumberPrefix', () => {
const dummyApp = {} as App;
const dummyFile = {} as TFile;
type TempTFile = TFile;
const dummyFile = {} as unknown as TempTFile;
const settings = {
eqNumberPrefix: 'Prefix-'
} as unknown;